📘
HPM Education - Haskell
  • Introduction to Haskell
  • Introduction
    • Functions
    • Functional Programming vs Imperative Programming
    • Installing Haskell
    • Haskell Modules
    • Loading Modules into GHCi
    • Expressions
    • Laziness
    • Immutability
  • Types in Haskell
    • Introduction
    • Basic Types
    • Static Type Check
    • Polymorphic and Overloaded Types
    • Data Structure Types
      • Lists
        • List Functions
      • Tuples
    • Function Types
      • Curried Functions
      • Partial Application
  • Defining Functions / Working with Functions
    • The Layout Rule
    • Local Definitions
    • The Infix Operator
    • Conditionals
      • If-then-else Statements
      • MultiWayIf
      • Guarded Equations
      • Case-of Statements
    • Pattern Matching
      • Tuple Patterns
      • List Patterns
    • Lambda functions
    • Function Operators
  • List Comprehensions
    • List Comprehensions
  • Higher-order Functions
    • Introduction
    • The map Function
    • The filter Function
  • Recursion
    • Introduction
    • 4 Steps to Defining Recursive Functions
    • Recursion Practice
    • Folds
      • Fold Right (foldr)
      • Fold Left (foldl)
  • Cutom Types
    • Declaring Types
      • Type Synonyms
      • Data Declarations
      • Newtype declarations
  • Type Classes
    • Introduction
    • Basic Classes
      • Eq – Equality Types
      • Ord – ordered types
      • Show – Showable Types
      • Read – readable types
      • Num – Numeric Types
      • Integral – Integral Types
      • Fractional – Fractional Types
      • Enum – Enumeration Types
    • Derived Instances
    • Exercise – Making a Card Deck Type
  • Interactive Programming
    • Introduction
    • Input / Output Actions
    • Sequencing Actions
    • Exercise - Numbers Guessing Game
  • Functors, Applicatives and Monads
    • Introduction
    • Functors
    • Applicative Functors
    • Monads
      • Maybe Monad
      • List Monad
      • Monad Laws
  • References / Further Reading
Powered by GitBook
On this page

Was this helpful?

  1. Defining Functions / Working with Functions
  2. Pattern Matching

List Patterns

PreviousTuple PatternsNextLambda functions

Last updated 2 years ago

Was this helpful?

As with tuple patterns, we can think of list patterns as a combination of two types of patterns as well. The first type is the patterns for each individual element we define which forms a list of patterns. The second type comes from the fact that that same list of patterns is a pattern itself. For example, let's say we want to define a function that takes in a list of any type and checks whether it contains exactly 4 elements:

check4 :: [a] -> Bool
check4 [_, _, _, _] = True
check4 _ = False

That is, we first check the pattern of [_, _, _, _], which is a list of four patterns itself, one for each individual element in the expected list, and for each of those we use the wildcard because we do not care about what the value of the element is. We only care that the element exists there and that there are exactly four elements in the list. If that first pattern doesn't match, we simply use the wildcard to say that whatever is there, we already know it does not contain exactly 4 elements and return False.

But there is also another list pattern that is very useful and it takes advantage of the cons (:) constructor used for constructing lists. We know that a list represented as [1, 2, 3] is actually constructed as 1 : 2 : 3 : [] using the (:) constructor, so we can use that form in our list patterns (but we must parenthesise patterns using cons):

check4 :: [a] -> Bool
check4 (_ : _ : _ : _ : []) = True
check4 _ = False

ghci> check4 [1,2,3]
False
ghci> check4 [1,2,3,4]
True

In fact, we can represent any list with at least one element with the following pattern:

(x : xs)

In this pattern, x is the first element of the list and xs is the tail of the list (whatever remains after excluding the first element). The xs can be a list of n number of elements or it could even be just an empty list [], in which case, the whole expression (x : xs) would be a list of just one element. Note that x and xs are simply standard names in this pattern, but we could use other names if we'd like to.

This means we can pattern match on lists with an undefined number of elements, unlike with tuples, where we have to match a finite number of elements. For example, the list functions head and tail use exactly this pattern to select the first element and the tail of a list, respectively:

head :: [a] -> a
head (x : _) = x

tail :: [a] -> [a]
tail (_ : xs) = xs

We said that the pattern (x : xs) holds true for any list with at least one element, so what happens if we call these functions on an empty list?

ghci> head []
*** Exception: Prelude.head: empty list

ghci> tail []
*** Exception: Prelude.tail: empty list

That case is covered by throwing an error since empty lists have neither a head nor a tail.

from before