[erlang-questions] pattern match test operator

Bob Ippolito <>
Tue Dec 4 19:42:37 CET 2007


On 12/4/07, Andras Georgy Bekes <> wrote:
> Hi,
>
> Almost every or every second day I bump into a situation when my
> proposed pattern-match-test-operator would be useful.
>
> > What I feel is that such a notation isn't bringing enough to the
> > language so that it's worth it.
> OK, I'm trying to generalise the idea, to bring more.
>
> Most of the time I'd use a pattern-match-test-operator, I'm about to do
> a disjunctive pattern matching. Something that could be solved
> by "fall-through á la C switch/case" like in the example written by
> somebody a few days ago:
>
> days_in(jan, _);
> days_in(mar, _);
> days_in(may, _);
> days_in(jul, _);
> days_in(aug, _);
> days_in(oct, _) -> 31;
> days_in(dec, _);
> days_in(apr, _);
> days_in(jun, _);
> days_in(sep, _);
> days_in(nov, _) -> 30;
> days_in(feb, common) -> 28;
> days_in(feb, leap) -> 29.
>
> We probably don't like the idea of fall-through, that's for imperative
> languages :-)
>
> Let's follow a more scientific approach: Pattern matching basically
> tests whether a term is a member of a subset of terms. So a pattern
> describes a subset of terms. Basically guards do the same, they
> describe a subset of terms. With the use of guards, we can narrow the
> subsets that we describe by a pattern, we intersect the subsets. Now
> with guards you can do something that you cannot do with patterns: you
> can do all the set-operations (union, intersection, subtraction) with
> the use of ',', ';' and all the boolean operators).
>
> Now let's try to extend patterns with the set operations.
>
> - Intersecting the sets of patterns is trivial, for example, the
> intersecion of the patterns {a,_,_} and {_,b,_} gives the pattern
> {a,_,_}={_,b,_}
>
> - Union: I think there is no '|' operator in Erlang, so:
> {a,_,_} UNION {_,b,_} could be written: {a,_,_}|{_,b,_}
> Now this was easy, let's do something tricky:
>
> {a,X,_} when is_integer(X) | {X,b,_} when X>3
>
> This would match if either of the patterns match. The binding rule for
> variables is well-defined, we already can have variables bound to
> different values in different branches of an 'if', 'case', or 'receive'
> expression.
>
> - Subtraction: it could be done by the pattern match test operator!
> For example:
> ({a,X,Y} when is_integer(X) | {X,b,Y} when X>3) when
>                                                 not(Y~={_,undefined})
>
> Probably there is a better sintax for all of this.
>
> About the semantics: all this is possible with some parse
> transformations. For example a pattern match with the above pattern:
> ({a,X,Y} when is_integer(X) |
>  {X,b,Y} when X>3) when not(Y~={_,undefined}) = VALUE
>
> is (logically) equivalent to
>
> begin
>    case VALUE of
>       {a,X,Y} when is_integer(X) ->
>          ok;
>       {X,b,Y} when X>3 ->
>         ok;
>       _ ->
>         erlang:error({badmatch,VALUE})
>    end,
>    case Y of
>       {_,undefined}->
>          erlang:error({badmatch,VALUE});
>       _->
>          VALUE
>    end
> end
>
> Now writing this parse transform is not impossible, but it could not use
> an elegant syntax and would be slower than a compiled implementation.
> But the real disadvantage: You cannot use it in 'real' patterns
> (function heads, case and receive expressions...) only in pattern-match
> expressions.
>
> Is this bringing enough to the language so that it's worth (considering)
> it?

Parse transforms happen at compilation time. What is your reasoning
behind "slower than a compiled implementation"? Compiling a module
that uses a parse transform might be insignificantly slower, but you
don't do that very often.

-bob


More information about the erlang-questions mailing list