Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

7. Lists, sets, and maps

A List a is a Python list, written with the same square brackets: [1, 2, 3]. The functions that work over lists live in a List module, so you call them as List.map, List.filter, List.fold. Keeping them behind a module name means a shared word like len can belong to every collection without clashing. Set and Map follow the same pattern and lower to Python’s set and dict.

let ns = [1, 2, 3, 4, 5]

let squares = List.map (fun x -> x * x) ns
let bigs = List.filter (fun x -> x > 2) ns
let total = List.fold (+) 0 ns
let doubled = List.map ((*) 2) ns

let third = Option.withDefault 0 (List.get 2 ns)
let missing = Option.withDefault 0 (List.get 9 ns)

let uniq = Set.ofList [1, 1, 2, 3]
let scores = Map.add "ada" 10 Map.empty

print squares
print bigs
print total
print doubled
print third
print missing
uniq |> Set.len |> print
[1, 4, 9, 16, 25]
[3, 4, 5]
15
[2, 4, 6, 8, 10]
3
0
3

fun x -> x * x is a lambda, the same anonymous function you met in lesson 2 written inline. Where the function is just an operator, an operator section is shorter: (+) is addition as a two argument function, and (*) 2 is multiplication with one argument already supplied, so List.map ((*) 2) ns doubles every element. List.fold walks the list carrying an accumulator, starting from 0 here and adding each element, which is how you reduce a list to a single value.

Totality carries over from lesson 3. In Python, ns[9] on a five element list raises IndexError. Pyfun has no bracket indexing at all. Instead List.get 9 ns returns an Option, None when the index is out of range, and you supply a fallback with Option.withDefault. The lookup cannot crash, because the empty case is a value you have to handle. Map.tryFind works the same way, returning an Option so a missing key is data rather than an exception.

Exercise

This program has two holes. List.fold needs a function to combine the running total with each element, and Option.withDefault needs a fallback for when index 10 is out of range. Run pyfun check to see the type each hole expects, then fill them so the program prints the sum and a safe default.

let ns = [4, 8, 15, 16, 23]

let total = List.fold ? 0 ns
let atTen = Option.withDefault ? (List.get 10 ns)

print total
print atTen

The checker reports:

note: hole `?` has type `int -> int -> int` — try: const, max, min — or: flip ?
 --> 3:23
  |
3 | let total = List.fold ? 0 ns
  |                       ^

note: hole `?` has type `int` — try: total — or: List.sum ?, String.len ?, cbrt ?, ceil ?
 --> 4:32
  |
4 | let atTen = Option.withDefault ? (List.get 10 ns)
  |                                ^
2 unfilled holes

Expected output:

66
0

Open in the playground

Show solution
let ns = [4, 8, 15, 16, 23]

let total = List.fold (+) 0 ns
let atTen = Option.withDefault 0 (List.get 10 ns)

print total
print atTen

The (+) section adds each element into the accumulator, and 0 is the default returned because index 10 is past the end of the list.