[erlang-questions] checking for unbound?

Jesper Louis Andersen jesper.louis.andersen@REDACTED
Sat Nov 20 16:57:38 CET 2010


On Sat, Nov 20, 2010 at 3:18 PM, Andy Kriger <andy.kriger@REDACTED> wrote:
> Is there a way to test if a variable is unbound?

I am a bit hesitant to guess at what you mean by "unbound variable".
Variables which are free (In the lambda-calculus terminology of
"free") lead to compilation errors since terms ultimately have to be
closed. Thus referral to a variable not existing in the scope or
failing to use a variable in a scope results in an error.

> I thought I could do something clever like...
> [E|EL] = mnesia:dirty_index_read(myTable, value, indexField),
> if <E is unbound> -> do something; % nothing was returned
>  length(EL) > 0 -> do something; % more than 1 item was returned
>  <E matches pattern> -> do something; % a match was returned
>  true -> do something % whatever is left

We can look up mnesia:dirty_index_read/3 via 'erl -man mnesia', and
see it returns a RecordList. Thus, pattern matching to the rescue:

case mnesia:dirty_index_read(T, v, i) of
  [] -> do something; % Nothing was returned
  [Obj] -> do something ; % Exactly one object was returned
  RL when is_list(RL) -> do something; % We got a whole list of things
end,

notice how the case match is exhaustive. It matches all possible
outcomes (and strictly speaking we could have been exhaustive without
the [Obj] match). A rule of Erlang is that any expression will
*always* return a value. There is no equivalent to C's "void" and no
"null" as well. Any identifier is always bound to a value or the
program is wrong. This concept is very powerful: you don't have to
fear the null pointer exception and you don't have to check for null
unless the program returns an atom playing the role of the concept
"null".

It is quite often the case that functional languages choose their
expressions in this way. In fact, it would be hard to do otherwise,
since it would remove you from the lambda-calculus basis.

> But I don't see a way to make the first test.

Pattern matching is the key to the puzzle. You let a specific value
play the role of being "unbound". In the above case, the role is
played by the empty list, [], in other cases it is played by an atom
-- usually 'undefined', 'none', 'aborted' -- and in some cases, it is
played by a complete term: '{error, Reason}', '{parse_error,
PartialParse}' and so on. An example of an atom, 'undefined', playing
the role is when asking the application module for configuration
options:

case application:get_env(MyModule, profiling) of
  undefined -> false
  {ok, Bool} when is_boolean(Bool) -> Bool
end,

Many imperative languages would have chosen to write a program along
the lines (somewhat Go-ish syntax),

V := application.GetEnvBool(MYMODULE, "profiling")
if (V == nil) {
  return false
} else {
  return V
}

because any identifier in these languages referencing an object is
essentially a pointer which can be nil/null. Personally, I find this
ugly, and cumbersome to write and I much prefer the functional
variant, of which Erlang is an example.



-- 
J.


More information about the erlang-questions mailing list