30

In OCaml, I was used to writing code which looked like:

let combine o1 o2 = match o1, o2 with
    | Valid, Invalid | Invalid, Valid -> Invalid
    | _ -> ...

I didn't find a way to write or-patterns in Haskell and I really miss it. Does anyone have a solution?

tshepang
  • 12,111
  • 21
  • 91
  • 136
  • 5
    `Valid, Invalid | Invalid, Valid` is called an or-pattern. Your question is clear (I think) but knowing the name may help answer it. – Pascal Cuoq Jul 11 '14 at 15:04
  • AFAIK Haskell does not have something like this, each case has to be separate. There are ways around this, but none of them that I can think of in 5 minutes are nearly as pretty as this OCaml example. – bheklilr Jul 11 '14 at 15:17
  • 1
    In fact, I have a way around in some cases. For instance, this feature is especially useful for dealing with symmetric arguments. In this case, you can match half the cases and add a last equation which perform a recursive call and reverse the order of the arguments. But it is not as nice, I agree... – Jonathan Laurent Jul 11 '14 at 15:20
  • I was wondering. That's fine when the pattern matching doesn't return any value but how OCaml will deal with thins like `| Valid a, Invalid | Invalid , Valid b ->` ... a or b ???? – mb14 Jul 12 '14 at 09:32
  • 1
    Ocaml will return an error. You can only use in the right hand size identifiers which have been defined in all patterns with the same type. – Jonathan Laurent Jul 13 '14 at 14:19

3 Answers3

21

I don't think this is possible in haskell. There are however, a few alternatives:

Factor out the common code with a where binding

This doesn't make much sense in your example, but is useful if you have more code in the body of the case expression:

combine o1 o2 = case (o1,o2) of
  (Valid, Invalid) -> handleInvalid
  (Invalid, Valid) -> handleInvalid
  ...
 where
  handleInvalid = ...

Use wildcard patterns

In my experience, it doesn't happen so often that you want to use two or patterns in one pattern match. In this case, you can handle all the "good" cases and the use a wild card pattern for the rest:

combine o1 o2 = case (o1,o2) of
  (Valid, Valid) -> Valid -- This is the only valid case
  _              -> Invalid -- All other cases are invalid

This has the disadvantage that it bypasses the exhaustiveness checker and that you cannot use wildcard patterns for other purposes.

Use guards and ==

If the types you want to match against are enum-like types, you might consider making an Eq instance. Then you can use == and || to match multiple constructors in one guard:

combine o1 o2
  | o1 == Invalid && o2 == Valid || o1 == Valid && o2 == Invalid = Invalid
  | ...

I agree that this doesn't look as nice and it also has the disadvantage of bypassing the exhaustiveness checker and doesn't warn you if patterns overlap, so I wouldn't recommend it.

bennofs
  • 11,873
  • 1
  • 38
  • 62
  • 3
    With an `Eq` instance, you can also write ``(o1,o2)`elem`[(Invalid,Valid), (Valid,Invalid)]``. But still, equalities aren't nice – pattern-matching is preferrable. – leftaroundabout Jul 11 '14 at 15:30
  • 2
    I love how, in the “`where`” solution, you can save a lambda where an OCaml programmer would naturally have inserted one. – Pascal Cuoq Jul 11 '14 at 16:24
11

You could use this quasiquoter http://hackage.haskell.org/package/OrPatterns. Your example translates to something like:

let combine o1 o2 = case (o1, o2) of
      [o| (Valid, Invalid) | (Invalid, Valid ) |] -> Invalid
      _ -> ...
aavogt
  • 1,308
  • 6
  • 14
3

There is a proposal to add or-patterns to GHC.

Until then (in addition to the other examples), pattern synonyms can be employed for similar means:

data ABC = A Int | B Int | C Bool

ab :: ABC -> Maybe Int
ab (A i) = Just i
ab (B i) = Just i
ab C{}   = Nothing

pattern AB :: Int -> ABC
pattern AB i <- (ab -> Just i)

If you want to match values of separate types (like Int and Bool) you can create some constrained existential type

data Showable where
  Showable :: Show a => a -> Showable

ac :: ABC -> Maybe Showable
ac (A i) = Just (Showable i)
ac (C b) = Just (Showable b)
ac B{}   = Nothing

pattern AC :: () => Show a => a -> ABC
pattern AC showable <- (ac -> Just (Showable showable))

showAC :: ABC -> String
showAC (AC showable) = "A or C: " ++ show showable
showAC (B i)         = "B: "      ++ show i
Iceland_jack
  • 6,848
  • 7
  • 37
  • 46