[erlang-questions] checking for unbound?
Alain O'Dea
alain.odea@REDACTED
Sat Nov 20 17:28:28 CET 2010
On Saturday, November 20, 2010, Jesper Louis Andersen
<jesper.louis.andersen@REDACTED> wrote:
> 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.
>
Short version:
[E|EL] = mnesia:dirty_index_read(myTable, value, indexField) is a
pattern match expression. All expressions in Erlang have values
(dirty_index_read must have some value, unbound is not possible). If
you get something other than a list with one or more elements you'll
get a crash with {error, bad_match} as the cause.
Jesper's case expression is a possible solution.
My style preference is to use multiple functions and pattern-matching:
%...
handle_read(mnesia:dirty_index_read(myTable, value, indexField)),
%...
handle_read([]) ->
% handle no items
handle_read([E]) ->
% handle one item
handle_read([_|_] = EL) ->
% handle many items
More information about the erlang-questions
mailing list