Haskell syntax for 'or' in case expressions

10,386

Solution 1

There is no way of sharing the same right hand side for different patterns. However, you can usually get around this by using guards instead of patterns, for example with elem.

foo x | x `elem` [A, C, G] = ...
      | x `elem` [B, D, E] = ...
      | otherwise          = ...

Solution 2

with guards:

factorial n
    | n < 2 = 1
    | otherwise = n * (factorial (n - 1))

with pattern matching:

factorial 0 = 1
factorial 1 = 1
factorial n = n * (factorial (n - 1))

Solution 3

I'm not entirely familiar with F#, but in Haskell, case statements allow you to pattern match, binding variables to parts of an expression.

case listExpr of
    (x:y:_) -> x+y
    [x]     -> x
    _       -> 0

In the theoretical case that Haskell allowed the same:

It would therefore be problematic to allow multiple bindings

case listExpr of
    (x:y:_) | [z] -> erm...which variables are bound? x and y? or z?

There are rare circumstances where it could work, by using the same binding:

 unEither :: Either a a -> a
 unEither val = case val of
   Left v | Right v -> v

And as in the example you gave, it could work alright if you only match literals and do not bind anything:

case expr of
  1 | 0 -> foo
  _     -> bar

However:

As far as I know, Haskell does not have syntax like that. It does have guards, though, as mentioned by others.

Also note:

Using | in the case statement serves a different function in Haskell. The statement after the | acts as a guard.

case expr of
  [x] | x < 2 -> 2
  [x] -> 3
  _ -> 4

So if this sort of syntax were to be introduced into Haskell, it would have to use something other than |. I would suggest using , (to whomever might feel like adding this to the Haskell spec.)

unEither val = case val of
  Left v, Right v -> v

This currently produces "parse error on input ,"

Solution 4

Building on some of the above answers, you can (at least now) use guards to do multiple cases on a single line:

case name of
    x | elem x ["Bob","John","Joe"] -> putStrLn "ok!"
    "Frank"                         -> putStrLn "not ok!"
    _                               -> putStrLn "bad input!"

So, an input of "Bob", "John", or "Joe" would give you an "ok!", whereas "Frank" would be "not ok!", and everything else would be "bad input!"

Solution 5

Here's a fairly literal translation:

factorial n = case n of
    0 -> sharedImpl
    1 -> sharedImpl
    n -> n * factorial (n - 1)
    where
        sharedImpl = 1

View patterns could also give you a literal translation.

isZeroOrOne n = case n of
    0 -> True
    1 -> True
    _ -> False

factorial1 n = case n of
    (isZeroOrOne -> True) -> 1
    n -> n * factorial (n - 1)

factorial2 n = case n of
    (\n -> case n of { 0 -> True; 1 -> True; _ -> False }) -> 1
    n -> n * factorial (n - 1)

Not saying that these are better than the alternatives. Just pointing them out.

Share:
10,386
Rahul Göma Phuloré
Author by

Rahul Göma Phuloré

Updated on June 17, 2022

Comments

  • Rahul Göma Phuloré
    Rahul Göma Phuloré almost 2 years

    In F#, I can use | to group cases when pattern matching. For example,

    let rec factorial n = 
      match n with
      | 0 | 1 -> 1                 // like in this line
      | _ -> n * factorial (n - 1)
    

    What's the Haskell syntax for the same?

  • Rahul Göma Phuloré
    Rahul Göma Phuloré over 12 years
    What if I have a bunch of cases, like outputting number of days in a given month?
  • newacct
    newacct over 12 years
    "which variables are bound? x and y? or z?" in OCaml and F# they require each pattern to bind the exact same bindings
  • Dan Burton
    Dan Burton over 12 years
    @newacct cool. Nice to know my theorizing was on the right track. I can't imagine it would be very hard to include this in Haskell, so I have no idea why they haven't yet.
  • luqui
    luqui over 12 years
    The "rare" occurrences where it would work are those occurrences that are meaningful -- the ones we would actually end up writing. So "rare" is not terribly diminutive here. However, I suspect in practical circumstances, it would pose a problem for typechecking, because v would have to be monomorphically bound, and in these cases I suspect the RHS would depend polymorphically on the type of v. I am not sure without analyzing real source code though.
  • Daniel Wagner
    Daniel Wagner over 12 years
    @luqui Is it possible that in the rare occurrences where it's meaningful, you could use record selector syntax to get what you want? That is, is it always the same fields of the same constructors that line up, or are there cases where sometimes it's (constructor one, field one; constructor two, field one) and sometimes it's (constructor one, field one; constructor two, field two)?
  • jmg
    jmg over 12 years
    How does using guards help sharing the right side of equations?
  • Jimmy Hoffa
    Jimmy Hoffa almost 12 years
    It would because the poster's scenario would have been: | x elem [0,1] = 1