[erlang-questions] Re: What atom name to represent a null value

Joe Armstrong erlang@REDACTED
Sat Feb 27 20:48:43 CET 2010


On Fri, Feb 26, 2010 at 8:40 AM, Tim Fletcher <mail@REDACTED> wrote:
>> It is defined in somewhere? Some documentation?
>>
>> Or just in common sense?
>
> I suspect it is a practical application of the theoretical notion of
> undefined in functional programming (and mathematics). For example,
> here's a little function:
>
>  foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.
>
> This function is only defined for the values 1, 2, and 3. It is
> undefined for any other value. You can use the theoretical notion of
> undefined when doing certain transformations on this function and so
> on.
>
> On a practical level, calling this function with any other value will
> throw an error, which isn't always helpful. What if you wanted to pass
> in any integer? In Erlang you can write this:
>
>  foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.
>  foo(_) -> undefined.

No no no .... ^ 100

For three reasons.

a) Think types. The type of

 >  foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.

is   int() -> int()

But the type of

>  foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.
>  foo(_) -> undefined.

is int() -> int() | 'undefined'  (which is a crazy type)

b) foo(4) has NO VALUE. but if you say foo(4() -> undefined. Then foo(4)
HAS a value (namely undefined).

The best way to express this in Erlang is as follows:

foo(1) -> 2;
foo(2) -> 3;
foo(3) -> 1;
foo(X) -> exit({ebadArgToFoo, X}).

This expresses the required relation that foo is oof type int()  -> int()
and that evaluation foo(4) is illegal and raises and exeception.

The the point of exit - it was designed for *exactly* this situation.

Now any good erlang programmer would not write foo like this, they would write

foo(1) -> 2;
foo(2) -> 3;
foo(3) -> 1.

And *nothing* else - since evaluating foo(4) will raise an exception and the
default exception will be sufficient to identify the error.

c ) There is also a worse problem. If you use the following:

>  foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.
>  foo(_) -> undefined.

Then calling foo(4) will not cause the program to crash *immediately* but
somewhat later. For example, suppose you write this:

    f(X) -> g(X) * 7.
    g(X) -> h(X).
    h(X) -> foo(X).

Now you call f(4). The program will crash in g/1, when evaluating undefined * 7,
and NOT when calling foo(4). This is two functions removed from where the error
occurred - and will make debugging difficult.

This is why we say things like 'let it crash' - you should also 'fail early' and
'fail fast'.

foo(1) -> 2;
foo(2) -> 3;
foo(3) -> 1.

is a correct program because it crashes if you call foo(4).

> foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.
>  foo(_) -> undefined.

Is an incorrect program because is succeeds if you call foo(4) returning
undefined.

> foo(1) -> 2;
>  foo(2) -> 3;
>  foo(3) -> 1.
>  foo(_) -> exit(badArg).

Is also a correct program since it crashes (ie raises and exception) when called
with foo(4).

Error handling in Erlang is supposed to be "not defensive"  you should only
write code for the expected cases. If you want to write error recovery code
enclose the entire function in a catch or try - or spawn link the process
to an error handling process and 'let some other process fix the error'

/Joe









>
> You can then call foo(4) and have it actually return a value, which
> means you can then write things like foo(X) =:= 1 without it throwing
> a badmatch error.
>
>
> Sometimes you may want to distinguish between values that aren't
> defined, and values that do exist but are null; in this case you can
> use undefined in combination with null/nil/none/nothing, as long as
> your values aren't atoms.
>
> If your values can be atoms then you'll probably need to use tagged
> tuples, something like this:
>
>  > datastore:lookup(foo).
>  undefined % key/value is not defined
>
>  > datastore:lookup(bar).
>  none % key exists, value is "null"
>
>  > datastore:lookup(baz).
>  {ok, undefined} % key exists, value is undefined
>
> The important thing is to be able to distinguish between the different
> cases.
>
>
> Hope that helps.
>
> Tim
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>
>


More information about the erlang-questions mailing list