I don't understand how Haskell can destructure the array to a tuple
e.g., why this works
head :: [a] -> a
head (x:xs) = x
But this does not work
head :: [a] -> a
head [x:xs] = x
it's unintuitive to me
CodePudding user response:
(x:xs) is not a tuple, (x, xs) is a pattern for a 2-tuple. (x:xs) is short for ((:) x xs) where (:) is (one of the two) data constructors for a list. The other one is [], the empty list.
The (:) data constructor has two fields: the first one (here x) is the head that points to the first item of the list, and the second one (here xs) points to the list of the remaining elements. So this looks like a linked list.
[x:xs] is a short variant of [(x:xs)], so it binds with a list of lists where the outer list contains a single element, and that single element matches with (x:xs) so a non-empty list with x the first item of the only sublist, and xs the remaining elements of that sublist.
CodePudding user response:
why this works
head :: [a] -> a head (x:xs) = xBut this does not work
head :: [a] -> a head [x:xs] = x
This is a very common source of difficulty for people learning Haskell. These look similar in some cases, but mean different things:
The type
[a]is equivalent to[] a, meaning “the type of lists of zero or more elements that all have typea”.The pattern
[p]is equivalent top : []and(:) p [], meaning “match a list of exactly one element that matches the patternp”.
Note that a list pattern with a variable at the end matches any number of remaining elements, but if it ends with “nil” [] then it matches a fixed number of elements. Here are some examples for lists of length n:
xs— n ≥ 0 — more than zero elements; any listp : xs— n ≥ 1 — more than one element; non-empty listsp₁ : p₂ : xs— n ≥ 2 — more than two elementsp₁ : p₂ : p₃ : xs— n ≥ 3 — &c.- …
[]— n = 0 — exactly zero elements; empty lists[p]— n = 1 — exactly one element; singleton lists[p₁, p₂]— n = 2 — exactly two elements[p₁, p₂, p₃]— n = 3 — &c.- …
Writing x : xs, which is the same as (:) x xs, matches a list of one or more elements. Putting that in parentheses (x : xs) is equivalent, it’s only necessary because of precedence rules: head x : xs means (head x) : xs, which is a valid expression but not a valid pattern.
Writing [x : xs], which is the same as (x : xs) : [] and (:) ((:) x xs) [], matches a list of exactly one element ([ e ]) where that element matches a list of one or more elements (e = x : xs). Here are some examples:
let { x : xs = [1] } in (x, xs)evaluates to(1, [])because:x:xsmatches1:[]x=1xs=[]
- And these are all equivalent:
[1]1 : [](:) 1 []
let { x : xs = [1, 2, 3] } in (x, xs)evaluates to(1, [2, 3])because:x:xsmatches1:2 : 3 : []x=1xs=[2, 3]
- And these are all equivalent:
[1, 2, 3]1 : [2, 3]1 : 2 : [3]1 : 2 : 3 : [](:) 1 ((:) 2 ((:) 3 []))
let { x : xs = "hi" } in (x, xs)evaluates to('h', "i")because:x:xsmatches'h':'i' : []x='h'xs="i"
- And these are all equivalent:
"hi"'h' : "i"'h' : 'i' : [](:) 'h' ((:) 'i' [])
let { [x : xs] = [[1]] } in (x, xs)evaluates to(1, [])because:[x : xs]matches[[1]]x:xsmatches1:[]x=1xs=[]
let { [x : xs] = ["yo"] } in (x, xs)evaluates to('y', "o")because:[x : xs]matches[["yo"]]x:xsmatches'y':'o' : []x='y'xs="o"
- Neither
x : xsnor[x : xs]matches an empty list[] [x : xs]does not match["ya", "no"]becausex : xs:[]does not match"ya":"no" : [], because[]does not match"no":[][x : xs]does not match[[]]becausex:xsdoes not match[]
