From HaskellWiki
Jump to navigation Jump to search

Inspired by

It is proposed that Haskell allow multiple pattern matches in a case statement to map to a single right-hand-side expression.

factorial :: Int -> Int
factorial n = case n of
  0, 1      -> 1
  _ | n < 0 -> undefined
  _         -> n * factorial (pred n)

-- without this suggested extension,
-- the cases of 0 and 1 would have to be handled separately.

If the right-hand-side expression utilizes bound variables from the pattern match, then all grouped pattern matches must bind the same variable.

unEither :: Either a a -> a
unEither eitherVal = case eitherVal of
  Left v, Right v -> v

This is because only one of the pattern matches will occur. Clearly, the RHS cannot draw from multiple differing pattern matches. This would be an error.

-- this would be a scoping error
eitherSum :: Either Int Int -> Int
eitherSum eitherVal case eitherVal of
  Left l, Right r -> l + r

An additional example:

-- modified example from Haskell School of Music
getDur :: Primitive a -> Dur
getDur p = case p of
  ModDur (Tempo r) d           -> d / r
  Note d _, Rest d, ModDur _ d -> d  
-- notice how we don't have to write `-> d` for
-- each of the 3 trivial cases; they all share the same RHS

-- layout rules should also permit
-- splitting the shared matches over multiple lines
getDur' :: Primitive a -> Dur
getDur' p = case p of
  ModDur (Tempo r) d -> d / r
  Note d _,
  Rest d,
  ModDur _ d -> d

The comma syntax would work for combining the outermost level of pattern matches in case statements. Something more interesting might be to allow nested options in patterns. Using `|` syntax:

case expr of
  (GT|LT):foo -> expr1
  EQ:bar      -> expr2

-- This could be written more verbosely with Haskell's guard syntax:
case expr of
  x:foo | x == GT || x == LT -> expr1
  EQ:bar -> expr2

-- Or by simply duplicating the RHS
case expr of
  GT:foo -> expr1
  LT:foo -> expr1
  EQ:bar -> expr2

Using `|` is a problem, however, since it would conflict with guard syntax. Using `,` is also a problem for nested options such as this, because it conflicts with pattern matching on tuples:

case expr of
  (GT,LT):foo -> expr1

Nested case pattern options may be too complex to actually be useful. But it could be convenient to have it at the outermost level, in which case, the comma syntax should work decently. There would still be some ambiguity when guards are included

case expr of
  pat1 | guard1, guard2 -> expr1

In the above case, it might be considered ambiguous whether or not guard2 is a guard, or whether it should actually be considered as pat2, another pattern.