Lists
Lists in milang are singly-linked cons cells, declared in the prelude as
List = {Nil; Cons head tail}. The bracket syntax is sugar that desugars into
this representation.
Constructing Lists
nums = [1, 2, 3, 4, 5]
empty = []
consed = 10 : 20 : 30 : []
build = {target = c, os = linux, arch = x86_64}
nums = [1, 2, 3, 4, 5]
empty = []
consed = [10, 20, 30]
[] is Nil, and [1, 2, 3] desugars to Cons 1 (Cons 2 (Cons 3 Nil)).
The : operator (cons) is right-associative.
Use range to generate a sequence:
a = range 1 6
b = range 1 11
build = {target = c, os = linux, arch = x86_64}
a = [1, 2, 3, 4, 5]
b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Accessing Elements
head, tail, last, and init all return Maybe values — Just x on
success, Nothing on an empty list. at returns Maybe for index access.
xs = [10, 20, 30]
a = head xs
b = tail xs
c = last xs
d = init xs
e = at xs 1
f = head []
build = {target = c, os = linux, arch = x86_64}
xs = [10, 20, 30]
a = Just {val = 10}
b = Just {val = [20, 30]}
c = Just {val = 30}
d = Just {val = [10, 20]}
e = Just {val = 20}
f = Nothing {}
len returns the number of elements:
a = len [1, 2, 3]
b = len []
build = {target = c, os = linux, arch = x86_64}
a = 3
b = 0
Transforming
map
Apply a function to every element:
doubled = map (\x -> x * 2) [1, 2, 3, 4, 5]
build = {target = c, os = linux, arch = x86_64}
doubled = [2, 4, 6, 8, 10]
filter
Keep elements satisfying a predicate:
evens = filter (\x -> x % 2 == 0) [1, 2, 3, 4, 5, 6]
build = {target = c, os = linux, arch = x86_64}
evens = [2, 4, 6]
fold
Left-fold with an accumulator:
total = fold (+) 0 [1, 2, 3, 4, 5]
build = {target = c, os = linux, arch = x86_64}
total = 15
reverse
backwards = reverse [1, 2, 3, 4, 5]
build = {target = c, os = linux, arch = x86_64}
backwards = [5, 4, 3, 2, 1]
take / drop
front = take 3 [1, 2, 3, 4, 5]
back = drop 3 [1, 2, 3, 4, 5]
build = {target = c, os = linux, arch = x86_64}
front = [1, 2, 3]
back = [4, 5]
zip
Pair up elements from two lists:
pairs = zip [1, 2, 3] [10, 20, 30]
build = {target = c, os = linux, arch = x86_64}
pairs = [[1, 10], [2, 20], [3, 30]]
enumerate
Produce [index, value] pairs:
indexed = enumerate ["a", "b", "c"]
build = {target = c, os = linux, arch = x86_64}
indexed = [[0, a], [1, b], [2, c]]
Combining Lists
joined = concat [1, 2] [3, 4]
appended = push [1, 2, 3] 4
build = {target = c, os = linux, arch = x86_64}
joined = [1, 2, 3, 4]
appended = [1, 2, 3, 4]
join concatenates a list of strings with a separator:
csv = join ", " ["alice", "bob", "carol"]
build = {target = c, os = linux, arch = x86_64}
csv = alice, bob, carol
Querying
xs = [1, 2, 3, 4, 5]
a = sum xs
b = product xs
c = any (\x -> x > 4) xs
d = all (\x -> x > 0) xs
e = contains xs 3
f = contains xs 99
build = {target = c, os = linux, arch = x86_64}
xs = [1, 2, 3, 4, 5]
a = 15
b = 120
c = 1
d = 1
e = 1
f = 0
Pipelines
Lists work naturally with the pipe operator for readable data processing:
result = range 1 11 \
|> filter (\x -> x % 2 == 0) \
|> map (\x -> x * x) \
|> sum
build = {target = c, os = linux, arch = x86_64}
result = 220
Pattern Matching on Lists
Match by exact length with [a, b, c], or match head and tail with
[first, ...rest]:
xs = [10, 20, 30, 40]
result = xs ->
[a, b, ...rest] = a + b
[] = 0
build = {target = c, os = linux, arch = x86_64}
xs = [10, 20, 30, 40]
result = 30
Recursive functions often pattern-match to walk a list:
mySum xs = xs ->
[x, ...rest] = x + mySum rest
[] = 0