[eeps] EEP XXX: Pattern-test operator

Erik Søe Sørensen eriksoe@REDACTED
Fri Apr 13 23:50:00 CEST 2012


Den 13. apr. 2012 02.46 skrev Richard O'Keefe <ok@REDACTED>:

>
> On 13/04/2012, at 11:51 AM, Erik Søe Sørensen wrote:
>
> > <EEP-pattern_test_operator.md>
>
> (0) Congratulations and thanks for providing a reference
>    implementation.
>
Thanks... the write-up was the more time-consuming part, and I appear to
have botched that job ;-)


> (1) PLEASE don't take the binary ~ operator; we need
>    that for frames, and we need frames URGENTLY.
>    The use of "~" is indeed backwards compatible
>    with respect to existing _code_ but not with
>    respect to existing _EEPS_.
>
Which EEP in particular?  I can't seem to find any mention of using tilde -
except on the mailing list.

(2) I don't understand why it is not sufficient to
>    allow <pattern> = <guard expression> as a guard
>    expression.  That would be a simple change that
>    would increase the simplicity of the language
>    by removing a constraint; this change makes the
>    language *more* complex.
>
>    The argument near the end I find unconvincing in
>    the extreme: = isn't *supposed* to return booleans,
>    and guards should never have had 'and' 'or' 'andalso'
>    or 'orelse' in the first place but should have nested
>    the existing ',' and ';'.  Guard tests drive control
>    NOT by returning true or false but by SUCCEEDING or
>    FAILING, and that's *exactly* what = does.
>

Guards drive control based on whether they evaluate to true or (fail /
evaluate to something else).
Try this:
  F = fun(X) -> case X of Y when Y -> yes; _ -> no end end.
  [F(true), F(false), F(other)].
I get [yes, no, no] - consistent with the "true or not" test; the shell and
the compiler agree on this.
This semantics is also simple in that guard functions behave the same
whether they occur within a guard or elsewhere.
Why should '=' behave any differently?


   It is already a readability problem that = can appear
>    deeply nested inside expressions, and the proposed
>    inverted ~ worsens this amazingly:  not only may
>    bindings be hidden deep inside surprising places,
>    they may be hidden in two opposing orders with
>    subtly different semantics.
>
> (3) Since we have the syntax <pattern> = <expression>,
>    requiring <expression> ~ <pattern> is very nearly
>    unforgivable.  The EEP confused me enormously
>    until I got my head around this inversion.
>

I'm sorry, but I find the other way around unreadable.
The expression-before-pattern form reads better aloud: "if E matches P..."
Also, most of Erlang has left-to-right evaluation - 'case', ',', 'when',
'andalso', 'orelse', 'try', and clause lists all follow this.


(4) case ... of
>      X  when  X ~ {ok,A}; X ~ [okay,A} -> ...A...
>    end
>
>    In "[okay,A}", is "[" a typo for "{"
>    or is "}" a typo for "]"?

The former - sorry, I hadn't noticed this. (The latter would also work,
though, but wasn't the intention.)


>  This can be handled
>    by a proposal I _think_ there is an EEP for to
>
   allow
>
>        case ...
>          of {ok,A}
>           ; {okay,A} -> ... A ...
>        end
>
At a quick glance, the closest one appears to be "unnesting cases". That
one doesn't allow that syntax, though; it's a different extension.


>
>    the generalisation being that
>        <pattern> [when <guard>]
>    is replaced by
>        <pattern> [when <guard>] {; <pattern> [when <guard>]}...
>
>    That may have been one of the EEPs I could not write up
>    because there is no serious definition of .md syntax and
>    semantics.
>
It probably is.

   case E0 ~ P of true -> E1; false -> E2 end
>
was part of a concocted counter-example.

   seems like a strange way to write
>
>        case E0
>          of P -> E1
>           ; _ -> E2
>        end
>
> (5) The "bindings are only accessible" part is not specified
>    precisely.  In particular, consider
>
>        (X ~ {foo,A} orelse X ~ {bar,A} orelse X ~ {ugh,A})
>        andalso ... A ...
>
>    Is some binding for A accessible after andalso, and if
>    not, why not?

Not as specified, no; the first operand of the 'andalso' is an 'orelse'
expression.
You're right that if the third rule is generalized (to cover the case where
all guards connected by ';' bind a variable), then the first rule should be
too (to cover the case where a number of subexpressions connected by
'orelse' bind a variable). I believe I felt that stating rules for handling
orelse/andalso in general would make the spec unnecessarily complex (from a
"persuading people that this is a good idea which is simple to implement"
viewpoint), and decided to postpone that part.
(I have handled the general case in another language, though.)


>  If it were written
>
>        case X
>          of {foo,A}
>           ; {bar,A}
>           ; {ugh,A} -> ... A ...
>        end
>
>    the variables available at the arrow would clearly and
>    simply be the intersection of the variables available
>    after each pattern/guard.
>
Agreed.


> (6) The EEP has very few examples that cannot be done as well
>    or better using Pattern = Guard_Expr in a guard and/or
>    the generalised case, both of which make disjunction of
>    pattern/guards readily expressible.  The main novelty
>    seems to be "anything but this".
>
>    For example,
>
>        receive
>           X when (      F = {'$gen_cast',_} = X
>                  orelse F = {'$gen_call',_} = X
>                  orelse F = ok
>                  ) andalso F = ok ->
>
>
>    neatly handles the "not a {'$gen_cast',_} or {'$gen_call',_}"
>    example, and all it needs is to allow Pattern = Expression in
>    a context where people already expect it to be allowed.
>
>    In general, I must say that I have learned over the painful
>    years to loathe and dread "anything but <this>" matches in
>    whatever language they are found.  If Erlang makes it hard
>    to express such proven bug-breeder, all the better for
>    Erlang.
>
>    The lack of negative patterns is a positive BENEFIT of the
>    way Erlang is now; sabotaging this aspect of Erlang would be
>    done at our peril.
>
Fair enough; I can't say that I regard the negation aspect as a
particularly strong selling point.

   All things considered, I think that the sharper the distinction
>    between pattern/guards and expressions the better; it helps to
>    keep Erlang readable in a way that anything-goes-anywhere doesn't.
>
We already have "some expressions may occur in guards".
And you propose allowing "pattern matching may occur in guards" yourself.
So it must be the "pattern matching may occur in non-guard expressions"
part that buggers you (right?).
Now, that is already allowed as well - just in the less syntactically
convenient form of "case E of P -> true; _ -> false end".  I haven't seen
anyone using such a construct as an operand for 'andalso' or similar -
because that would certainly qualify as unreadable - but I fail to see why
pattern-based branching could not be used in this light-weight way, instead
of always having to occur in more verbose constructs.


> (7) We keep on getting odd limited or dangerous proposals for
>    things that are neatly and simply solved by abstract patterns.
>    For example,
>
>        #int_point(X, Y)
>            when is_integer(X), is_integer(Y)
>              -> {X, Y}.
>
>        #two_int_points(X = #int_point(_,_),
>                        Y = #int_point(_,_))
>              -> {X, Y}.
>
>    Yes, the abstract patterns EEP notes that allowing patterns in
>    guards is a prerequisite.  That mean we ALREADY have an EEP that
>    proposes patterns in guards.  But that meant the existing =
>    with the existing semantics; just a restriction removed.

Except that it isn't that compatible with existing semantics.


>    This new proposal is far more elaborate than the abstract
>    patterns EEP requires, far more confusing, and far more dangerous.
>    Once we have abstract patterns, there would be little or no
>    reason to _use_ the inverted ~ in actual source code.
>
That could be true.

(8) The thing I don't get a feeling for is how _much_ of a problem
>    this EEP solves.  When similar things have come up in the past
>    I have asked for concrete examples, and have generally been
>    able to show that it is possible to rewrite the real code in
>    existing Erlang with good clarity without new features.
>    (Or with abstract patterns as the only new feature.)
>

I'd very much like to hear others' opinions: how can we add a feature like
this (or like EEP14, which is apparently the other candidate) in a way that
fits well into the existing language and feels natural and not "confusing"
or "inverted" (or even "dangerous")?

(It may well be that the most peaceful way ahead is for me to make the
pattern test operator work internally in the compiler, without any source
syntax form, and then the implementation of EEP 29 can proceed... :-) )

/Erik
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/eeps/attachments/20120413/74e79e3b/attachment.htm>


More information about the eeps mailing list