[eeps] EEP XXX: Pattern-test operator
Mon Apr 16 01:57:38 CEST 2012
On 14/04/2012, at 9:50 AM, Erik Søe Sørensen wrote:
> (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.
I do apologise: it's the frames proposal, which was never
The proposal has been around for years and has OFTEN been
mentioned in the mailing list. Joe Armstrong's "proper
structs" proposal, which is very similar, is also similar
in not having been EEPified and in having a use for "~",
though not the same use.
> (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.
That is a comparatively recent (and ill-advised) extension;
the Erlang Specification does not admit a GuardExpr as a
Guardtest (section 6.20.1, page 104).
There are really only two configurations for the language that
make sense: (a) make a clear distinction between guards and
expressions and *keep* the distinction clear; (b) make NO
distinction between guards and expressions. "How long will
you halt between two opinions?"
> 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.
Sigh. Guard functions should never have been allowed 'elsewhere'.
That was a huge mistake we are still paying for.
However, it simply isn't true that guard functions behave the same in
all contexts, as I hope we all understand.
> Why should '=' behave any differently?
Because it should behave *usefully*.
Because we seriously need to allow Pattern = Expression in list
comprehension and have that taken as success/failure rather than
as computing true or false. It is ridiculous that people have
to write P <- [E] instead of P = E.
As a matter of fact, it would be a serious help if there were a
compiler warning turned on by default about any use of = to
compute a value: X = f(Y).
> 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.
Then find some other language in which the basic pattern
matching form is not Pattern = Expression.
> The expression-before-pattern form reads better aloud: "if E matches P..."
That violates the English language I am familiar with,
where patterns match expressions but never the other way around.
(Like the way some people nowadays talk about assigning variables
to values. Feh!) The values of expressions *conform to* patterns
but don't match them.
I mean, what is so hard about pronouncing "if P matches E"?
As it happens, I used Pop-2 a lot, and I often to this day use a
language (R) where a left-to-right assignment is available.
(In R you can write var <- expr or expr -> var. meaning the same.)
I have nothing against value-before-binder per se.
What I have much against is inconsistency in a single language.
Of course, there's also the fact that SML, O'Caml, F#, Clean,
Haskell, Mercury, and indeed all the functional languages I know
have pattern = expression or val pattern = expression or
let pattern = expression, but _always_ pattern on the left,
expression on the right. So there is a cultural inconsistency
to worry about as well. But I think you could get away with that
IF AND ONLY IF Erlang did not use pattern = expression.
> Also, most of Erlang has left-to-right evaluation - 'case', ',', 'when', 'andalso', 'orelse', 'try', and clause lists all follow this.
Irrelevant, since in pattern = expression there is only *one* evaluation,
that of the expression. Since the pattern is not evaluated, there is no
evaluation order issue.
In the case of 'when', it is difficult if not impossible to tell what the
evaluation order is, and I have always assumed that the compiler is free
to re-order tests.
> 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.
That's because pattern matching always *was* pattern of guards in the
languages Erlang got guards from, in which pattern matching was (and is)
never an expression in the first place.
> So it must be the "pattern matching may occur in non-guard expressions" part that buggers you (right?).
Was "bugger" a failure of idiom for "bug", or an intended
reference to anal intercourse?
Not a bit of it. Pattern = Expression *is* allowed in non-guard expressions
and always has been. Although it would be an excellent thing if there were
a compiler option to scream loudly any time the value of a match was used,
turned on by default.
What bugs me is, as I have already said:
(1) Using a new operator for pattern matching when we already have one
that would work perfectly well, if allowed to;
(2) putting the operands of that operator in the opposite order of the
(3) further blurring of the distinction between guards and expressions;
(4) abstract patterns (which were proposed a *long* time before the EEP)
address a lot of isssues, and I dread nickle-and-diming attacks on
isolated cases postponing the Real Thing still further.
> 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.
You also haven't seen anyone doing
-define(M(E,P), case E of P -> true; _ -> false end).
... ?M(1+1, X) andalso
have you? In short, there isn't an actual practical problem here.
> 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.
It's perfectly compatible with the Erlang Specification semantics.
> (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:
I don't want to hear opinions. I want *evidence*. How much of a
problem do we actually have? I want *concrete examples* of code
that could be improved by using this new form and could not be
improved as well or better some other way.
> 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")?
EEP 14 was in fact the EEP that would allow the 'generalised case' I wrote of.
> (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... :-) )
I really really wish there were better documentation of BEAM.
Being able to *read* BEAM is not the same as being able to *write* it.
More information about the eeps