A programming convention
Joe Armstrong
joe@REDACTED
Tue Jun 11 11:15:27 CEST 2002
On Tue, 11 Jun 2002, Richard Carlsson wrote:
>
> On Tue, 11 Jun 2002, Joe Armstrong wrote:
>
> > find violates the "principle of least surprise" and
> > should have returned {ok, V} |{error, Why}>>
> > [...]
> > Now the convention. We always provide a *pair* of interface functions:
> >
> > lookup(Key, Dict) -> Value | exit(Why)
> > lookupQ(Key, Value) -> {ok, Value} | {error, Why}
>
> I don't think {error, Why} is a good idea in cases like this.
> First, let me say that I really do advocate a standardised maybe()
> type for Erlang programs.
Yes but there seems to be a case for a maybe() type and an expected/unexpected behaviour
type.
We seem to need three types to denote intentionality (what the programmer meant)
maybe() ::= {value, X} | none
For situations where I know that something might not have a value and I'll
make no attempt at handling the unexpected behaviour.
might() ::= {ok, Value} | {error, Why}
For situations where I know that something might not work and I may wish
to handle the unexpected behaviour. Example handling {error, enofile} in file:open
must() ::= Value | EXIT(Why)
I require the expected behaviour and want to crash if I cannot continue.
<< aside
if a function is written thus:
foo(123) ->
case X of
... ->
abc;
... ->
exit(bla)
end.
How should I document its type, like this???
+type foo(int()) -> abc | EXIT(bla)
>>
I think the *important* thing is that *everybody* uses the standard types
and NOT their own variants.
One misplaced {yes, X} | no (in the middle of code which otherwise returns
{value, V} | none) plays havoc with your programming style.
<flame>
For years I have found code that returns one of two values.
many small things like switches which had a state (on|off)
colored lights (red|green)
sexes male | female
Please (a million times)
*change* your code to work with BOOLEANS and rename your function appropriately.
example:
sex(Person) -> male | female
Should be changed to
is_female(person) -> true | false.
etc.
If you no this you will be able to use all the nice generics in the standard libraries,
the alternative is zillions of small conversion routines.
If you *insist* on data abstraction you could always use (local)
macros to achieve the same thing!
</flame>
> However I don't think it should be mixed
> with error descriptors. If it _is_ an error, then let it remain an
> exception.
I agree
If it is expected behaviour, then the "Why" part is
> not relevant: typically (if you program with the maybe() pattern),
> there is exactly one reason for "error", and any other exceptions
> that may be raised during execution are _real_ errors. Suppose for
> instance that you have a bug in your dictionary implementation which
> suddenly triggers - do you want this to be transformed into a term
> {error, Why}? (Probably, the application will then just toss away the
> Why part and continue as if the key was not present. Bad thing.)
>
Yup.
> I usually use:
>
> maybe() ::= {value, X} | none
>
> which I think has a more neutral look-and-feel than "ok" and "error",
> even though "value" is 3 chars longer than "ok". :-]
>
> For instance, it makes it look less weird to store a maybe() type in
> a record field or similar:
>
> X#foo{a = {value, 42}, b = none, c = lookupQ(Key, Dict)}
>
> ("ok" and "error" would definitely seem strange here.)
>
> In dictionary-like functions, I myself generally use "lookup" for the
> maybe()-version, and "get" for the exception-raising version.
> A naming convention for indicating that a function returns a maybe()
> type might or might not be a good thing; I don't feel so sure that it
> applies to other cases than dictionary lookup. In many cases you
> might want to write functions that do return a maybe() type, but
> which don't really have (or need) an exception-raising version. Should
> you still feel obliged to name the function "fooQ" although there will
> never be a "foo"?
maybe()
(At least I wouldn't have to look up the manual page all the time)
>
> > lookup(Key, Dict) ->
> > case (catch lookupQ(Key, Dict)) of
>
> As a final comment regarding efficiency: Yes, exceptions are supposed to
> be exceptional, but not extremely so. Even if you change to zero-cost
> setup (which I believe that we have in HiPE these days), it must still
> remain feasible to use "throw" for nonlocal returns - being able to do
> so is part of the programming model.
>
> /Richard
>
>
> Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.)
> E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/
> "Having users is like optimization: the wise course is to delay it."
> -- Paul Graham
>
>
/Joe
More information about the erlang-questions
mailing list