[erlang-questions] Floating guard sequences
Richard O'Keefe
ok@REDACTED
Fri Feb 20 00:53:52 CET 2009
On 20 Feb 2009, at 9:22 am, Zvi wrote:
>
>
> fess-3 wrote:
>>
>> ...
>> floating guards on the LHS of a match seem like they could make
>> code much more concise and readable, and erlang-like [ to me ]
>>
>>
>> { X when is_integer(X), [ "x" | Rest ] } = string:to_integer(Str),
>> { Y when is_integer(Y), _ } = string:to_integer(Rest),
>>
>
> much more readable, if we can marry patterns with guards:
>
> { X = int(), [ "x" | Rest ] } = string:to_integer(Str),
> { Y = int(), _ } = string:to_integer(Rest),
>
> where int() is patten matching any integer value.
int() would be a function call, which doesn't make much sense
in a guard.
This is a rather interesting example, because
- string:to_integer/1 and string:to_float/1
ARE in the on-line documentation
- they ARE accepted by 'erl'
- but they are NOT in lib/stdlib/src/string.erl
They both return either {Number,Tail} or {error,Cause}.
I have to say that this interface is brain-dead.
When a function can return one of several outcomes,
it should be possible to tell one from another by a simple
pattern match. A *good* result would be
{ok,Number,Tail}
or {error,Cause}
The solution is not to bloat the pattern sublanguage beyond
all recognition, but to fix the broken interface.
better_to_integer(String) ->
case string:to_integer(String)
of {error,Cause} -> {error,Cause}
; {Number,Tail} -> {ok, Number,Tail}
end.
better_to_float(String) ->
case string:to_float(String)
of {error,Cause} -> {error,Cause}
; {Number,Tail} -> {ok,Number,Tail}
end.
and then to write a plain
{ok,X,[$x|Rest]} = better_to_integer(Str),
{ok,Y,_ } = better_to_integer(Rest)
Testing whether X or Y is an integer ASKS THE WRONG QUESTION.
A better question is "is this not the atom 'error'"?
But the best question is the one we really want: "is this
conversion ok?" And fixing the interface lets us ask very
simply what we really mean.
There's a common pattern I'm seeing in discussions like this.
There is some ugly code, but people accept the *cause* of the
ugliness and just want to put a glossy coat of paint over
the *symptoms*.
It's a basic principle of good data structure design in Prolog,
which has carried over without change to Erlang, that when a
data structure could be one of several alternatives, you
should be able to recognise each of the alternatives with a
single *positive* pattern match. (That is, nothing should
be recognised by virtue of not being something else.) When I
reimplemented the internal data structures of the Quintus
Prolog compiler to follow this principle, not only was the
code easier to read, the compiler ran 20% faster.
> Or, if X and Y unused, like in the example above:
>
> { int(), [ "x" | Rest ] } = string:to_integer(Str),
> { int(), _ } = string:to_integer(Rest),
It's *still* better to write
{ok,_,[$x|Rest]} = better_to_integer(Str),
{ok,_,_ } = better_to_integer(Rest)
By the way, the pattern ["x"|Rest] should be
[$x|Rest] as I have it, or "x"++Rest.
More information about the erlang-questions
mailing list