exported variables (was: RE: How to make GS Widgets)

Jay Nelson jay@REDACTED
Sat Apr 17 04:48:40 CEST 2004


At 08:19 PM 4/16/04 +0200, Ulf Wiger wrote:

>... is how how I would write it.  (:
>
>validate_colors({R, G, B}) ->
>    VC = fun(C) -> validate_color(C) end,
>    {VC(R), VC(G), VC(B)}.
>
>validate_color(C) when is_integer(C), C > 0, C < 255 ->
>    C.

I assume you intended another clause for the non-valid
cases to avoid the program failing with bad_match:

validate_color(_) ->
     0.

Problem is you've changed the behavior.  Consider what
happens with validate_colors({-1, 52, 45}).  Your version
will either fail on bad_match or will return {0, 52, 45} if you
add the clause I mentioned above.  I think the original
intent was to return {0, 0, 0} if any of the values is invalid
or the shape of the tuple is wrong.



In general, though, this exercise pointed out a technique
for developing code as a beginning functional programmer.
The problem is that most people come from an imperative
or procedural background and have already learnt the
brute force straight-ahead coding that the original case
statement exemplifies.  It leads to convoluted functional
code but is the natural imperative way of attacking the
problem.  I would suggest the following approach:

1) Assume no invalid data and code the most clear and
direct functions possible.

2) Once #1 is working, consider the most obvious and
egregious errors and code the test for them as separate
functions.

3) Test all #2 functions standalone in the interpreter.

4) Extend #1 with #2 functions by successive refinement:

a_function(WithPossiblyBadData) ->
    NotEgregiouslyBadData = egregious(WithPossiblyBadData),
    MuchCleanerData = eliminate_end_cases(NotEgregiouslyBadData),
    GoodToGo = all_other_bad_cases(MuchCleanerData),
    a_function_accepting_only_good_data(GoodToGo).


5) If it works and is fast enough leave it alone, otherwise
try to make it faster by profiling where the issues are.


The most "revolutionary" concept for new programmers is
the conversion from a procedural style to a functional style.
Don't try to solve the whole problem.  Get yourself part way
there and then do it again on the remaining part (this is the
easiest way to understand recursion as well).

The series of suggestions from the original to Ulf's final
version is a plausible trace of a real developer's "refactoring"
of functional code.  The less concise intermediate versions
simplify testing and reveal the issues at the heart of solving
the problem.  Removing redundant code, simplifying functions
and verifying that the results are the same all qualify as
step #5 tasks.

jay





More information about the erlang-questions mailing list