On 16/04/2012, at 1:05 PM, Michael Truog wrote:
> So you believe that pattern matching in guards requires binding local variables to be useful, right?
No, I just believe that it makes them *more* useful.
> You do not seem to be concerned about this change causing guards to create side-effects, where they previously where unable to do so.
Come off it! To start with, binding a variable is not a side effect.
But consider: apart from 'if', guards are not found on their own, but
has one part of a Pattern when Guard combination. If the Pattern part
can bind variables, what is so evil about the Guard part doing so?
> Are we just not aware or recognizing any potential negative aspects of binding variables within guard statements, or do we just accept them as a necessary evil?
I don't know who "we" are here. I can imagine no negative aspects of binding variables
within guards that are not also negative aspects of binding variables within patterns.
Erlang was strongly influenced by Strand 88 which owed a fair bit to Parlog.
In Parlog, a clause has the form
H := G1, ..., Gm : Body.
where ":" is the "commit" operator, and is left out if m = 0, the Gi being
the guards., and a Body is
- Body1 , Body2 "parallel AND"
- Body1 & Body2 "sequental AND"
- a call
Parlog guards are rather more general than Erlang guards, and it was
implementation experience with nested layers of processes that suggested
the simplified "flat" guards found in GHC, FCP, and Erlang. But in all
the concurrent logic programming languages, including GHC, FCP, and Parlog,
guards may include pattern-matches that bind variables.
I never heard of any negative consequences of _that_.
> To me, the idea of guard statements binding variables seems to contradict both the purpose/name "guard" since it is guarding state, and binding a variable within the guard statement would be allowing the state to leak out.
There are no guard statements, only guard tests. And pattern matching is
a perfectly good test. Consider two clauses:
f(X = Y) when true -> g(X).
f(Y) when X = Y -> g(X).
How does the second clause "leak" any "state" that the first one doesn't?
For that matter, these days Haskell has "pattern guards":
<clause head> | <pattern> <- <expr>{, ...} = <expr>
See http://www.haskell.org/haskellwiki/Pattern_guard
Nobody in the Haskell community thinks of this as "leaking" any kind of "state",
and Haskell is notably purer than Erlang...
>> I don't want to push this point if it isn't regarded as a problem, but I just wanted to understand how you see it.
Here's how I see it:
(1) The concurrent programming languages preceding Erlang allowed
pattern matches in guards, using "=". There were no conceptual
difficulties with this.
(2) I have never seen any explanation of why Erlang didn't.
(3) It is technically possible to rewrite Erlang clauses with
pattern matching to ones without, at a great loss of clarity.
f(Args) when T1, P = E, T2 -> Body
=>
f(Args) when T1 -> try E of P when T2 -> Body
catch _ -> rest_f(Args) end;
f(Args) -> rest_f(Args).
f'(Args, P) when T2 -> Body;
f'(Args, _) -> rest_f(Args).
I hope that gives you the idea. No "state" can be "leaked" by
pattern guards that cannot be "leaked" in this way.
(4) Pattern guards in Haskell don't seem to cause any problems.