Haskell Cheatsheet

Pattern Matching

Use this Haskell reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Pattern Matching Basics

isZero :: Int -> Bool
isZero 0 = True
isZero _ = False

Patterns are tried from top to bottom. _ matches anything and ignores it.

Matching Lists

safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x

sumList :: [Int] -> Int
sumList [] = 0
sumList (x:xs) = x + sumList xs

x:xs means a non-empty list with head x and tail xs.

Tuples and Nested Patterns

swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)

addPair :: (Int, Int) -> Int
addPair (a, b) = a + b

Patterns nest to any depth — match constructors inside constructors in one equation:

firstOfJust :: Maybe [a] -> Maybe a
firstOfJust (Just (x:_)) = Just x
firstOfJust _            = Nothing

startsWith :: Maybe (Int, String) -> String
startsWith (Just (0, s)) = "zero: " ++ s
startsWith (Just (_, s)) = s
startsWith Nothing       = ""

(Just (x:_)) matches a Just whose payload is a non-empty list, binding its head — no manual unwrapping needed.

Case Expressions

describe :: Maybe Int -> String
describe value =
  case value of
    Nothing -> "missing"
    Just n  -> "value: " ++ show n

Use case when pattern matching inside an expression.

Guards in Case Alternatives

classify :: Maybe Int -> String
classify value =
  case value of
    Nothing          -> "missing"
    Just n | n < 0     -> "negative"
           | n == 0    -> "zero"
           | otherwise -> "positive"

Each case alternative can carry guards. If every guard fails, matching falls through to the next alternative.

Let and Where

circleArea r =
  let radiusSquared = r * r
  in pi * radiusSquared

bmi weight height = category
  where
    value = weight / height ^ 2
    category
      | value < 18.5 = "underweight"
      | value < 25   = "normal"
      | otherwise    = "other"

let is an expression. where attaches helper definitions to an equation.

As-Patterns

describeAll :: String -> String
describeAll s@(first:_) = "The string " ++ s ++ " starts with " ++ [first]
describeAll [] = "Empty string"

s@pattern keeps both the whole value and the matched parts.

Lazy (Irrefutable) Patterns

lazyPair :: (Int, Int) -> Int
lazyPair ~(a, b) = 1  -- never forces the pair

splitFirst :: [Int] -> (Int, [Int])
splitFirst ~(x:xs) = (x, xs)  -- crashes only if x or xs is used on []

~pattern always matches without evaluating the argument; the bindings fail only when actually used. Useful for avoiding needless evaluation in mutually dependent definitions.

View Patterns

{-# LANGUAGE ViewPatterns #-}
import Data.List (stripPrefix)

greet :: String -> String
greet (stripPrefix "Hello, " -> Just name) = "Hi " ++ name
greet _ = "Who are you?"

With ViewPatterns, (f -> pattern) applies f to the argument and matches the result — handy for matching on a computed view instead of raw structure.