[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