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

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 
	  ... -> 
	  ... ->

  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.


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.


	sex(Person) -> male | female

Should be changed to

	is_female(person) -> true | false.


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!





> 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.)


> 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"?


(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


More information about the erlang-questions mailing list