case expressions

Contents
Match on shape
case is an expression
The wildcard
Nested cases

Match on shape

case inspects a value and runs the first alternative whose pattern fits. It is the tool for deciding on the structure of data rather than on a boolean condition:

classify :: Maybe Int -> String classify v = case v of Nothing -> "empty" Just 0 -> "zero" Just n -> "number " ++ show n

Patterns are tried top to bottom, so the literal zero must come before the variable pattern that would otherwise catch it. A variable pattern matches everything.

case is an expression

Like if, a case produces a value, so it can be an argument or the right side of a binding:

label = "value: " ++ case lookup 1 table of Nothing -> "missing" Just v -> show v

Indentation matters. The alternatives line up under each other, and the whole case belongs to the expression that started it.

The wildcard

An underscore matches anything and binds nothing. Use it when the value is irrelevant, and never use a fresh variable name for the same purpose, because a named variable silences the unused binding warning:

ok :: Either String Int -> Bool ok r = case r of Right _ -> True Left _ -> False

A trailing _ -> branch turns the match into a partial function. It is fine for a small enumeration and dangerous for a growing sum type, where a new constructor should break the build rather than fall into the default.

Nested cases

Two cases in a row are a smell. Usually the pattern can carry both levels at once, or the inner decision belongs in its own function:

-- flat: both shapes in one pattern pair :: Maybe (Maybe Int) -> Int pair (Just (Just n)) = n pair _ = 0

Function equations with several patterns are the same machinery as a case, only with a shorter syntax. The next lesson compares the two forms directly.

Next: Pattern matching

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: