A programming convention

Joe Armstrong joe@REDACTED
Tue Jun 11 08:50:50 CEST 2002


Here's an idea for a programming convention....

This convention has to do with the return value/exception problem.

The standard example is from dict.erl.

Suppose you search for a keyed value in a dictionary. Not finding a value
means one of precisely two things:

	1) A logical error in the program
	2) An expected behaviour of the application

In case 1) the application should terminate with an exception. In case 2)
the application should deal with the return value.

For this reason there are two functions which search for keys in dict.
They are

	dict:fetch(Key, Dict) -> Value | EXIT
	dict:find(Key, Dict) -> {ok,Value}| error

I can never remember which is which so I always have to consult the manual
page.

The programmer should use fetch if not finding a key is a logical programming
error otherwise find.

<<aside find violates the "principle of least surprise" and
should have returned {ok, V} |{error, Why}>>

An alternative would to *always* use find, and write

	{ok, Val} = dict:find(....)

In the case where a missing key represented should cause the program to crash.
The problems with this are twofold:

	a) You don't get a nice execution reason (badmatch)
	b) you need a lot of them in your code

i.e. you don't really want to write

	{ok, _} = io:format(...)
	{ok, _} = io:format(...)

	etc.

Now the convention. We always provide a *pair* of interface functions:

	lookup(Key, Dict)   -> Value | exit(Why)
	lookupQ(Key, Value) -> {ok, Value} | {error, Why}

The "Q" in the name signifies "Query" or "Question" in other words we do not
*know* if the function will work or not - this is the expected behavior
of the function.

Without the Q the function must work. The relationship is obvious

	lookup(Key, Dict) ->
	    case (catch lookupQ(Key, Dict)) of
		{'EXIT', Why} ->
			{error, Why};
		Other ->
			{ok, Other}
	    end.

We also say that functions with a Q at the end of the name *always*
return maybe() types << maybe() = {ok, Val} | {error, Why} >>

If we do all this we will *finally* be able to define an error in a program
as a "top level untrapped exit"

Comments welcome

...

	/Joe






More information about the erlang-questions mailing list