6.7.5. Or-Patterns¶
- OrPatterns¶
- Since:
9.12.1
Allow use of or-pattern syntax.
Or-patterns are enabled by the language extension OrPatterns
.
They allow condensing multiple patterns into a single one.
Suppose we have some sum type and code matching on it:
data Sweet = Cupcake | Liquorice | Cookie | Raisins
tasty Cupcake = True
tasty Cookie = True
tasty _ = False
Let us say we need to add another constructor to our type, like Cheesecake
.
Because of the wildcard pattern we used when defining tasty
, the compiler
doesn’t warn us that the pattern match might need adjustment, resulting in
cheesecake incorrectly being characterised as untasty.
If we want the compiler to aid us in Haskell2010, we must write out all cases
explicitly, vertically bloating the code.
This is where Or-patterns help. With OrPatterns
we can write:
tasty (Cupcake; Cookie) = True
tasty (Liquorice; Raisins) = False
If we extend Sweet
by another constructor, we’ll now get a warning
about a non-exhaustive pattern match -– given we compile with
-Wincomplete-patterns
.
Or-patterns are particularly useful in pattern matches that need to handle a high number of constructors. It is not uncommon to see pattern matches that deal with dozens of constructors, e.g. in GHC’s own source code (Pat.hs). In such cases, the only options are:
to use a wildcard and at the expense of clarity, and risking bugs when adding new constructors
to enumerate each constructor, at the expense of duplicating the code of the RHS
to use an Or-pattern
6.7.5.1. Specification¶
An or-pattern looks like this:
(pat_1; ...; pat_n)
where pat_1
, …, pat_n
are patterns themselves. Or-Patterns are
ordinary patterns and can be used wherever other patterns can be used.
The result of matching a value x
against this pattern is:
the result of matching
x
againstpat_1
if it is not a failurethe result of matching
x
against(pat_2; ...; pat_n)
otherwise.
The current main restriction on or-patterns is that they may not bind any variables or constraints. This prohibits code like
value :: Either a a -> a
value (Left x; Right x) = x -- binds a variable
or
data G a where
G1 :: Num a => G a
G2 :: Num a => G a
bar :: G a -> a
bar (G1; G2) = 3 -- cannot solve constraint `Num a`
data GADT a where
IsInt1 :: GADT Int
IsInt2 :: GADT Int
foo :: a -> GADT a -> a
foo x (IsInt1; IsInt2) = x + 1 -- cannot solve constraint `Num a`
This is so simply because we have not proposed yet a more general static semantics for such or-patterns.
So what can or-patterns do?
Apart from reducing code size and duplication, they compose with all forms of existing patterns, like view patterns and pattern synonyms:
f :: (Eq a, Show a) => a -> a -> Bool
f a ((== a) -> True; show -> "yes") = True
f _ _ = False
small (abs -> (0; 1; 2); 3) = True -- -3 is not small
small _ = False
type Coll a = Either [a] (Set a)
pattern None <- (Left []; Right (toList -> []))
empty None = False
empty _ = True
Or-patterns do not employ backtracking when given guarded right hand sides, i.e.
when one alternative of the or-pattern matches, the others are not tried when
the guard fails. The following code yields "no backtracking"
:
case (True, error "backtracking") of
((True; _); (_; True)) | False -> error "inaccessible"
_ -> error "no backtracking"
(The exact syntax and semantics of or-patterns are found here.)