Here are some more thoughts about qualifier lists as guards (this time
starting with the less important ones).
Heribert.
1. I agree that my "simplify" example was not so good. In fact, one
could do without nested qualifiers/guards easily because the first
two qualifiers
"s <- Just (simplify e )" and "s' <- Just (simplify e')"
have failure-free patterns, so that they could be replaced by
definitions of s and s' in a "where" clause. So hopefully the
following example with some test before the recursive calls is a bit
more convincing:
simplify (Plus e e') | someTest e e',
s <- simplify e ,
s' <- simplify e' | (Val 0) <- s = s'
| (Val 0) <- s' = s
| otherwise = (Plus s s')
simplify e = e
2. A list of qualifiers happens to work nicely in the "clunky" example,
because the test "ok1 && ok2" is a conjunction and in a way the
members of a list of qualifiers are also connected cunjunctively.
(What can we learn from this observation???)
3. From Alex' example (version 5a) one could have learned that we do not
need guards at all, because all sorts of guards (today's Haskell
guards, Peyton-Jones-style guards, and nested guards) can be replaced
by the Maybe monad in a *straightforward* way:
clunky env var1 var2 = fromJust (do val1 <- lookup env var1,
val2 <- lookup env var2
Just val1 + val2
++
Just (var1 + var2) )
simplify e = fromJust (do (Plus e e') <- e
guard (someTest e e')
s <- Just (simplify e ),
s' <- Just (simplify e'),
(do (Val 0) <- Just s ; Just s
++
do (Val 0) <- Just s'; Just s'
++
Just (Plus s s'))
++
Just e)
According to this, one might consider abolishing guards in function
definitions entirely. Guards (and even multiple equations for
defining a function!) could be seen as a relic from the times when
there was no syntactic support for monads in Haskell.
I don't advertise such a solution, e.g., because it is not backward
compatible. I just pointed it out because Maybe has been used as an
argument against the need for nested guards.
4. Another argument against nested guards has been that they are
"against the spirit of independent guarded equations". This is true,
but isn't it also against this spirit that one may write
f pats | guard1 = expr1
...
| guardn = exprn
instead of
f pats | guard1 = expr1
...
f pats | guardn = exprn
?
With traditional guards, matching consists of two major parts:
pattern matching + boolean tests. Factoring out common subexpressions
is possible only at the border of these two parts. With Simon's
proposal boolean tests and matching may be more freely mixed, but
factoring out is still only allowed after some prefix of matching
operations but not later.
This gave me the impression that the proposal might have been more
orthogonal in this respect and has lead to the suggestions in my
previous mail.
5. The more I think about it, the more I am in favour of Simon's
qualifier lists (i.e., without nesting), but with semantics according
to the Maybe monad. It has several advantages (the last one being the
most important one in my eyes):
- The syntax is not a problem:
clunky env var1 var1 | val1 <- lookup env var1,
val2 <- lookup env var2
= val1 + val2
...other equations for clunky...
- It is as backward compatible as Simon's proposal.
- It does not need the explananation of a new semantics for "<-" with
a non-monadic expression on the RHS.
- It does allow nesting for those who want it, because they can use
"++" on the RHS of "<-". (Note that this is even more flexible than
the "nested qualifiers" approach.)
- What are the possible results of pattern matching? It may fail or
succeed, and in the latter case some data is available. This is
exactly what Maybe has been introduced for.
6. I admit that my considerations in this message are not mainly driven
by the desire to solve some concrete programming need (and certainly
not in an ad-hoc way), but more by the desire to keep the language
orthogonal (although you might even question this).