In `A new view of guards', Simon cited

> [2] W Burton, E Meijer, P Sansom, S Thompson, P Wadler, "A (sic) extension
>         to Haskell 1.3 for views", sent to the Haskell mailing list
>         23 Oct 1996

Was that posted on this Haskell mailing list?  Does anyone know where I could
get a copy of this proposal?

> I would really welcome feedback on this proposal

Here goes, ...

Reaction to Pattern Guards
==========================

      * I am sure that I have been nobbled by the lack of pattern guards
        regularly but have got used to clunking my way round the problem.  I
        think I could get comfortable with pattern guards quickly.  I agree
        (with Simon) that pattern guards look a much better bet than views.

      * Putting guards into the where clause would break my declarative
        and operational model of Haskell.

      * Guards correspond (almost) to if-then-else expressions but this would
        no longer be the case with pattern guards.  Should Haskell have a
        conditional that expresses this way of choosing?

      * An aspect that I like of Haskell and the report is the translational
        semantics used to account for most of its constructions, particularly
        pattern matching.  How would this work out?


My Own Gripe: if-then-else
==========================

I have longstanding dislike of Haskell's expression conditional:

      * The use of keywords makes it visually unappealing and quite hard to
        pick out if-then-else expressions, unless keywords are set in bold
        face.

      * The use of three keywords to express something that can be expressed
        with a single function application seems extravagant.

      * It is not easy to get a layout for if-then-else that is entirely
        satisfactory.  My standard format is

                if test_expression
                   then success_part
                   else failure_part

        which takes up three lines and allows either of the branch expressions
        to spill onto successive lines, with each such line being a tab stop in
        from the `if' column.  The problem comes when several conditions are
        present, provoking a rightward march.  In this case I use

                if test1 then
                        part1
                else if test2 then
                        part2
                else
                        part3

        but I don't like it (the conditional expressions are not lined up and
        they tend to be buried in thickets of else-if-then keywords).

      * The interaction between if-then-else and do layout doesn't always work
        out so well -- the first arrangement above works but the second one
        doesn't (all of the expression must stay on the right of the opening
        `if' when the conditional is used as a `do' statement, otherwise layout
        will terminate the statement early).  The following is wrong:

             do if test1 then
                        part1
                else if test2 then
                        part2
                else
                        part3

        This seems to be a gotcha for non-expert Haskell users.


Sometimes I have felt tempted to write

        case test_expression of
          True -> success_part
          False -> failure_part

(and I know somebody who *does* do this) or even

        case () of
          () | test1 -> part1
             | test2 -> part2
             | otherwise -> part3

which has led me to wonder about an alternative `if' syntax.


An Alternative Conditional
==========================

Proposal: add the following conditional expression:

        case
          | test1 -> part1
          | test2 -> part2
          | otherwise -> part3

It works well with multi-way conditionals and `do' expressions; it is easy to
lay out; it stands out visually; it almost looks as if it is there already.
The layout rule shouldn't be needed here.

I suspect that the syntax should force the last clause to be an `otherwise'
conditional, entailing the conversion of `otherwise' to full keyword-hood.  As
such, if existing programs redefined `otherwise' or used them in non-guard
contexts then they would be broken, but easily fixable.  Otherwise it should be
upwards compatible with Haskell 1.4.  if-then-else could be phased out (or
retained I suppose).

Of course, this syntax would pave the way for including pattern guards in
conditionals.


Pattern Guards and Conditionals
===============================

If case conditionals were added at the same time as pattern guards, would they
make it any easier to account for them?  I suspected so and tried it out.
(This is a first cut; I am sure it can be improved.)

Syntax
------

The following productions would be added to the syntax (`exp^10' is extended;
`alt' and `gdpat' supplant the existing ones; `quals' and `cgdpat' are new).

@@@
exp^{10}        ->      \cdots
                |       case cgdpat
                        \cdots

alt             ->      pat @->@ exp [@where@ decllist]
                |       pat (gdpat|cgdpat) [@where@ decllist]

quals           ->      qual \{ @,@ qual \}

gdpat           ->      @|@ quals @->@ exp [ gdpat ]

cgdpat          ->      gdpat @|@ @otherwise@ @->@ exp
@@@@

Note that guarded patterns (gdpat) and complete guarded patterns (cgdpat) are
being distinguished syntactically.  I say that this is a good thing as there is
an important semantic difference.


Semantics: Case Expressions
---------------------------

These rules would replace rule (c) of `Semantics of Case Expressions' in the
Haskell report.  [Incidentally, I think there is a problem with the placement
of ';'s in this rule in the report.]

Rule (c)(i): completing alternatives

case v of { p | g where { decls }; _ -> e }

= case e of { y ->      (where y is a completely new variable)
        case v of { p | g | otherwise -> y where { decls }; _ -> y }}

   where `| g' is a guarded pattern (gdpat)


Rule (c)(ii): separating (complete) guards from alternatives

case v of { p | cg where { decls }; _ -> e }

= case v of { p -> let { decls } in case | cg; _ -> e }

    where `| cg' is a complete guarded pattern (cgdpat)

The first rule converts incomplete guards into equivalent complete guards and
the second rule lifts complete guards into conditionals.


Semantics: Conditionals
-----------------------

These are easy enough.


(a) convert to standard form

case | g_1       -> e_1
     | ...
     | otherwise -> e

= (\v -> case | True, g_1 -> e_1
              | ...
              | otherwise -> v) e

   where v is a completely new variable


(b) convert into two way conditionals

case | g_1       -> e_1
     | ...
     | otherwise -> v

= case | g_1       -> e_1
       | otherwise -> 
                case | ...
                     | otherwise -> v


(c) eliminate guard qualifiers

case | qs, e'    -> e
     | otherwise -> v

= case | qs, True <- e' -> e
       | otherwise      -> v


(d) eliminate let qualifiers

case | qs, let { decls } -> e
     | otherwise -> v

= case | qs -> let { decls } in e
       | otherwise -> v


(e) eliminate pattern qualifiers

case | qs, p <- e' -> e
     | otherwise   -> v

= case | qs        -> case e' of { p -> e; _ -> v }
       | otherwise -> v


(f) done

case | True      -> e
     | otherwise -> v

= e


Conclusion
==========

It looks as if case conditionals may help explain pattern guards in separating
them out from the pattern matching machinery.  In doing so it would make for a
more uniform treatment of a new construct in addition to cleaning up the older
if-then-else expressions.


Chris Dornan                    [EMAIL PROTECTED]
University College Cork         +353 21 902837



Reply via email to