[erlang-questions] Guards syntax for multiple values

Fred Hebert mononcqc@REDACTED
Wed Mar 27 03:42:45 CET 2019


I do not really want to get in an argument here, but I decided to 
rewrite these as I would with today's Erlang, for a comparative purpose.

On 03/27, José Valim wrote:
>
>validate_restart(_Name, Strategy)
>    when is_member([permanent, temporary, transient], Strategy) ->
>
>  ok;
>
>validate_restart(Name, Strategy) ->
>
>  error({bad_strategy, Name, Strategy}).
>
>

validate_restart(Name, Strategy) ->
    case lists:member(Strategy, [permanent, temporary, transient]) of
        true -> ok;
        false -> error({bad_strategy, Name, Strategy})
    end.

>
>*Example 2: rewriting Erlang binary operators into Elixir AST*
>
>rewrite_guard_call(Op)
>    when is_member(['band', 'bor', 'bnot', 'bsl', 'bsr', 'bxor'], Op) ->
>
>  {'.', meta(), [bitwise, Op]};
>
>rewrite_guard_call(Op) ->
>
>  ...N other clauses...
>

This one is just harder to plainly rewrite. As you said, it's a good 
example.

One way I could think of is to write up the types in a map; the pattern 
underlying this is of categorizing operators.

{OpList, Type} --> #{Op1 => Type, Op2 => Type, ...}

which can be done once at system initialization, and possibly use 
persistent terms now.

Then the function can be something like

rewrite_guard(Op) ->
    TypeMap = fetch_cached(...),
    rewrite_guard(Op, maps:get(Op, TypeMap)).

rewrite_guard(Op, bitwise) -> {'.', meta(), [bitwise, Op]};
... N other clauses ...

WHile this is far more obtuse than declaring the mapping inline and 
testing it on each run, the lookups are likely to be much faster at 
run-time since they won't have to do a linear search. That would of 
course depend on the map size and the cost of copying it (if you're not 
using persistent terms).

I guess with macros you could essentially build that map by enumerating 
clauses and baking them into a module for a similar effect, turning 
things into another autogenerated piece of code.

>
>*Example 3: Checking if a character is unreserved according to URI (RFC
>3986)*
>
>Similar to the Unicode example except we are pinned against a RFC, so it
>can't change:
>
>char_unreserved(Char)
>    when Char >= ?0, Char =< ?9;
>         Char >= ?A, Char =< ?Z;
>         Char >= ?a, Char =< ?z;
>
>         is_member("~_-.", Char) ->
>  true;
>
>char_unreserved(Char) when Char >= 0, Char <= 16x10FFFF ->
>
>  false.
>

This one is a bit annoying because you can just inline the boolean 
expression:

char_unreserved(Char) ->
    (Char >= ?0 andalso Char =< ?9 andalso
     Char >= ?A andalso Char =< ?Z andalso
     Char >= ?a andalso Char =< ?z andalso
     lists:member(Char, "~_-."))
    orelse
    (Char >= 0 andalso Char =< 16#10FFFF).

>
>And similar for reserved characters:
>
>char_reserved(Char)
>    when is_member(":/?#[]@!$&\'()*+,;=", Char) ->
>
>  true;
>
>char_reserved(Char) when Char >= 0, Char <= 16x10FFFF ->
>
>  false.
>

Which similarly inlines to:

char_reserved(Char) ->
    lists:member(Char, ":/?#[]@!$&\'()*+,;=")
    orelse
    (Char >= 0 andalso Char <= 16#10FFFF).

It is not my intent to push the discussion backwards or in any other 
way, One could argue that having to move the boolean expressions and 
pattern matches out of the function heads and guard patterns is a 
limitation on its own that should be addressed, and that the 
rewrite_guard is way too painful as it is: clarity is better than the 
potential performance gain (which also requires far more knowledge of VM 
internals than the is_member/2 approach).

I'm not casting judgement here.

Regards,
Fred.



More information about the erlang-questions mailing list