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!
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
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
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
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