Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Elm Destructuring (or Pattern Matching) cheatsheet

Should be work with 0.18

Destructuring(or pattern matching) is a way used to extract data from a data structure(tuple, list, record) that mirrors the construction. Compare to other languages, Elm comparatively supports less destructuring than other functional languages, but lets see what Elm does have!

Tuple

myTuple = ("A", "B", "C")
myNestedTuple = ("A", "B", "C", ("X", "Y", "Z"))

let
  (a,b,c) = myTuple
in 
  a ++ b ++ c
-- "ABC" : String

let
  (a,b,c,(x,y,z)) = myNestedTuple
in
  a ++ b ++ c ++ x ++ y ++ z
-- "ABCXYZ" : String

Make sure to match every tuple(no more no less) or you will get an error like:

let
  (a,b) = myTuple
in
  a ++ b
-- TYPE MISMATCH :(

In Elm community, the underscore _ is commonly used to bind to unused element.

let
  (a,b,_) = myTuple
in 
  a ++ b
-- "AB" : String

It's also more elegant to declare some constant of your app using destructuring.

-- with no destructuring
width = 200
height = 100

-- with destructuring
(width, height) = (200, 100)

Thanks to @robertjlooby, I learned that we can match exact value of comparable. This is useful when you want to explicitly renaming the variable in your branches of case .. of.

isOrdered : (String, String, String) -> String
isOrdered tuple =
 case tuple of
  ("A","B","C") as orderedTuple ->
    toString orderedTuple ++ " is an ordered tuple."
    
  (_,_,_) as unorderedTuple ->
    toString unorderedTuple ++ " is an unordered tuple."


isOrdered myTuple
-- "(\"A\",\"B\",\"C\") is an ordered tuple."

isOrdered ("B", "C", "A")
-- "(\"B\",\"C\",\"A\") is an unordered tuple."

Exact values of comparables can be used to match when destructuring (also works with String, Char, etc. and any Tuple/List/union type built up of them) - @robertjlooby

List

Comparing Lists to Tuples, Lists barely support destructuring. But Lists can be used to find the first element in a list, by utilizing the cons operator, ie ::w

myList = ["a", "b", "c"]

first list =
  case list of
    f::_ -> Just f
    [] -> Nothing

first myList
-- Just "a"

This is much more cleaner than using List.head but at the same time increases the codebase's complexity. By stacking up the :: operator, we can also use it to match the first, second or remaining values.

listDescription : List String -> String
listDescription list =
 case list of
    [] -> "Nothing here !"
    [_] -> "This list has one element"
    [a,b] -> "Wow we have 2 elements: " ++ a ++ " and " ++ b
    a::b::_ -> "A huge list !, The first 2 are: " ++ a ++ " and " ++ b

Record

myRecord = { x = 3, y = 4 }

sum record =
  let
    {x,y} = record
  in
    x + y

sum myRecord
-- 7

Or more cleaner:

sum {x,y} =
  x + y

Notice that the variable declared on the left side must match the key of record:

sum {a,b} =
  a + b

sum myRecord
-- The argument to function `sum` is causing a mismatch.

As long as our variable matches one of the keys in the record, we can ignore the other.

onlyX {x} =
  x

onlyX myRecord
-- 3 : number

I don't think Elm support destructuring in nested record (I tried) because Elm encourages sparse record

When destructuring a record, you do not have to declare all fields within the braces. Also, you can alias the whole record while destructuring it as a parameter. This is useful if you need shorthand access to some fields but also to the record as a whole.

  • nmk
myRecord = { x = 1, y = 2, z = 3}

computeSomething ({x, y} as wholeRecord) =
    -- x and y refer to the x and y fields of the passed in record
    -- wholeRecord is the complete record
    -- i.e. x and wholeRecord.x refer to the same field
    -- but z is only accessible as wholeRecord.z

Union Type

Again, thanks to @robertjlooby, we can even deconstruct the arguments of a union type.

type MyThing
  = AString String
  | AnInt Int
  | ATuple (String, Int)

unionFn : MyThing -> String
unionFn thing =
  case thing of
    AString s -> "It was a string: " ++ s
    AnInt i -> "It was an int: " ++ toString i
    ATuple (s, i) -> "It was a string and an int: " ++ s ++ " and " ++ toString i
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment