To understand this, it is first important to understand how lists are represented in F#. An F# list is either:
- an empty list written as
[]
or - a value (head) followed by another list (tail) written as
head::tail
So if you write, for example, [ 1; 2; 3 ]
you are actually constructing a list containing 1, followed by a list containing 2, (etc.) followed by an empty list. The expression is compiled to:
1::(2::(3::[]))
And you can omit the brackets and write just 1::2::3::[]
.
Pattern matching uses exactly the same syntax, but in the opposite direction. Instead of constructing lists, you are decomposing them. So when you have a pattern x::xs
it means that you want to take the first element and assign it to a variable x
and the remaining list should be assinged to a variable xs
.
The pattern (x::xs)::xss
is a bit more tricky, because it works on lists of lists. This means that the head of the list you match on is also a list. You could rewrite the code to the following simpler version:
let pack xs =
let collect x = function
| head::xss -> // Decompose into first element (head) and the rest (tail)
match head with
| y::xs when x = y -> (x::y::xs)::xss
| _ -> [x]::xss
| xss -> [x]::xss
List.foldBack collect xs []
Now you have some duplication in the code, but you can see that collect
takes x
and another parameter, matches that another parameter against head::xss
(to get the head/tail) and then also decomposes the head
.