ITT8060 Advanced Programming · Autumn 2026
Tallinn University of Technology
Full prose version with runnable examples: notes.html
ITT8060Last week: a set as a list without duplicates, and the invariant was yours to keep. This week the library keeps it — and records get the rest of their story.
The complete programs — map colouring, the cash register three ways, the exercises — are in the notes under Worked examples.
ITT8060
ITT8060A record names the parts of one thing: a name and an age — the AND-type beside lecture 4's OR-type unions.
A set promises membership only: order and repetition are meaningless.
A map is a finite function: every key has exactly one value, and a key is looked up, not searched for.
A list promises order and allows repetition: use it when the position matters.
ITT8060The tags on a blog post?
A set — a tag is present or absent; "fsharp, fsharp" is not two tags.
Student number → name?
A map — a number identifies one student; the lookup is the program.
A shopping basket?
A map from article code to count — 3 herrings + cheese + 2 herrings is cheese + 5 herrings.
The neighbours of a country?
A set of pairs, or a map from country to its set of neighbours — say which lookup you need. (Concept 5 faces exactly this choice.)
ITT8060
ITT8060A set is a collection of elements: \(\{\text{Jüri}, \text{Mall}, \text{Mari}\}\), \(\{1, 3, 5, 7, 9\}\), \(\mathbb{N}\), \(\mathbb{R}\). Order and repetition are of no concern.
Membership is decidable: \(\text{Mart} \notin \{\text{Jüri}, \text{Mall}, \text{Mari}\}\) and \(7 \in \{1, 3, 5, 7, 9\}\). The empty set: \(\{\}\) or \(\emptyset\).
\(A \subseteq B\) if every element of \(A\) is in \(B\); \(A = B\) if and only if \(A \subseteq B\) and \(B \subseteq A\).
Set-builder: \(\{ x \in A \mid p(x) \}\), for example \(\{1, 3, 5, 7, 9\} = \{ x \in \mathbb{N} \mid \text{odd}(x) \text{ and } x < 11 \}\).
ITT8060
\(A \cup B = \{ x \mid x \in A \text{ or } x \in B \}\) · \(A \cap B = \{ x \mid x \in A \text{ and } x \in B \}\) · \(A \setminus B = \{ x \in A \mid x \notin B \}\)
Set.union, Set.intersect and Set.difference compute exactly these.
With names1 = set ["Jüri"; "Mall"; "Mari"] and names2 = set ["Mari"; "Mart"; "Tõnu"],
Set.difference names1 names2 is set ["Jüri"; "Mall"].
ITT8060A type together with operations, where the representation of the values is hidden.
For sets: operations to generate a set from elements, to extract them again, and the standard operations. Why the first two?
Last week insert had to keep the list duplicate-free. Set.add does
it for you — and you cannot break it.
Inside set [1; 3; 5; 7; 9]: a balanced tree you never see.
ITT8060What is set [3; 1; 9; 5; 7; 9; 1]?
set [1; 3; 5; 7; 9]
Duplicates go; the elements come out ordered.
Is set ["Jüri"; "Mall"; "Mari"] = set ["Mari"; "Jüri"; "Mari"; "Mall"]?
true
Equality ignores order and repetition — as the mathematics says.
ITT8060> set [sin; cos];;
set [sin; cos];;
-----^^^
stdin(1,6): error FS0001: The type ''a -> 'a'
does not support the 'comparison' constraint.
For example, it does not support the
'System.IComparable' interface
Why does set [sin; cos] not compile?
A set keeps its elements ordered, so the element type needs
comparison — and functions have none.
The same constraint lecture 3 met in ordText sin cos.
Every Set and Map signature carries when 'a: comparison: on the
elements of a set, on the keys of a map.
ITT8060A map from \(A\) to \(B\): a finite subset \(A'\) of \(A\) with a function \(m : A' \rightarrow B\). Its domain: \(\mathrm{dom}\,m = A'\).
key value
a0 b0
a1 b1
...
a(n-1) b(n-1)
\(a_i\) is a key, \((a_i, b_i)\) an entry, \(b_i\) the value for \(a_i\).
A map is a finite function: a key has exactly one value.
ITT8060let reg1 =
Map.ofList [ ("a1", ("cheese", 25)); ("a2", ("herring", 4))
("a3", ("soft drink", 5)) ]
What does Map.add "a1" ("brie", 30) reg1 contain?
map [("a1", ("brie", 30)); ("a2", ("herring", 4));
("a3", ("soft drink", 5))]
A present key is overridden: a key has one value.
What are Map.tryFind "a9" reg1 and Map.find "a9" reg1?
None
KeyNotFoundException: The given key was not present in the dictionary.
tryFind lets the caller decide; find and reg1["a2"] raise on a missing key.
ITT8060
ITT8060 List Set Map
from a list the list itself Set.ofList xs Map.ofList kvs
to a list the list itself Set.toList s Map.toList m
add one x :: xs Set.add x s Map.add k v m
remove one List.filter ((<>) x) xs Set.remove x s Map.remove k m
membership List.contains x xs Set.contains x s Map.containsKey k m
exists List.exists p xs Set.exists p s Map.exists p m
forall List.forall p xs Set.forall p s Map.forall p m
filter, map List.filter p xs Set.filter p s Map.filter p m
The Map versions pass the key and the value separately: fun k v -> ....
ITT8060 List Set Map
fold List.fold f e xs Set.fold f e s Map.fold f e m
foldBack List.foldBack f xs e Set.foldBack f s e Map.foldBack f m e
size List.length xs Set.count s Map.count m
smallest List.min xs Set.minElement s Map.minKeyValue m
lookup List.tryFind p xs — Map.tryFind k m
set algebra — Set.union, intersect, difference
Map lookup, three ways: Map.tryFind k m, Map.find k m, m[k].
The complete lists: the Set module and the Map module documentation.
ITT8060What is Set.fold (-) 0 (set [1; 2; 3])?
-6
\(((0 - 1) - 2) - 3\): lecture 4's left-nesting fold, with the set's
ordering in place of the list's positions.
With small = Map.ofList ["b", 2; "a", 1], what is
Map.foldBack (fun k v l -> (k, v) :: l) small [] — a fold that visits
the keys from the largest down and conses each?
[("a", 1); ("b", 2)]
Key order, whatever order the entries went in.
ITT8060
ITT8060Last week: a record is a tuple with named fields —
{ name = "Tõnu"; age = 12 }, p.age, { p with age = 13 }, record
patterns. Today: what records are for.
A discriminated union says a value is one case or another. A record says a value has this part and that part.
Wlaschin: OR-types and AND-types. A domain model is built from both — and most data needs nothing else.
ITT8060type Country = string
type GMap = Set<Country * Country>
let areNb (c1: Country) (c2: Country)
(m: GMap) =
Set.contains (c1, c2) m
|| Set.contains (c2, c1) m
let areNb' c1 c2 m =
Set.contains (c1, c2) m
|| Set.contains (c2, c1) m
val areNb: c1: Country -> c2: Country
-> m: GMap -> bool
val areNb': c1: 'a -> c2: 'a -> m: Set<'a * 'a>
-> bool when 'a: comparison
Both compile, both work. The first says what a GMap is; the second
says the code does not care. When the types are the model, state them.
Labels drive inference the same way: with two record types sharing
name, let f p = p.name picks the latest type — annotate to say
which you mean.
ITT8060type Person = { name: string; age: int }
let mari = { name = "Mari"; age = 12 }
let jüri = { name = "Jüri"; age = 40 }
What is compare mari jüri?
3
Field by field, in declaration order: name decides first.
So records satisfy comparison and can be set elements or map keys.
What is set [mari; jüri; mari]?
set [{ name = "Jüri"
age = 40 }; { name = "Mari"
age = 12 }]
Two elements, ordered by name.
ITT8060type StudentId = StudentId of int
type Student = { id: StudentId; name: string }
type Students = Map<StudentId, Student>
let kati = { id = StudentId 1; name = "Kati" }
let rein = { id = StudentId 2; name = "Rein" }
let liis = { id = StudentId 3; name = "Liis" }
let students: Students =
Map.ofList [ kati.id, kati; rein.id, rein
liis.id, liis ]
A student is a record: an id and a name. StudentId is a one-case
union — lecture 4's wrapper type — so an id is not an int.
Students is a map because the id identifies the student: the lookup
is the program (concept 1).
What is students[StudentId 2].name?
"Rein"
The types say what the domain is — the style of Domain Modeling Made Functional.
ITT8060
ITT8060type Course = string
type Enrolments = Map<Course, Set<StudentId>>
let enrolments: Enrolments =
Map.ofList
[ "ITT8060", set [kati.id; rein.id; liis.id]
"ITI0210", set [rein.id; liis.id] ]
Which students take a course? A course code identifies the course; the students taking it are a set — nobody takes a course twice, and their order means nothing.
So Enrolments maps a course to a set: one lookup gives the whole
class.
(kati, rein, liis: the students of the previous slide.)
ITT8060let enrolled (c: Course) (e: Enrolments) =
match Map.tryFind c e with
| Some ss -> ss
| None -> Set.empty
let enrol (c: Course) (s: StudentId)
(e: Enrolments) =
Map.add c (Set.add s (enrolled c e)) e
let e2 = enrol "ITI0210" kati.id enrolments
enrolled answers who takes this course? A course nobody has taken
yet is the empty set — no exception, no None to carry around.
enrol records a student: look up, add to the set, put back. The map is
immutable, so the result is a new Enrolments.
What is enrolled "ITI0210" e2?
set [StudentId 1; StudentId 2; StudentId 3]
ITT8060let classmates (c: Course) (s: StudentId) =
enrolled c >> Set.remove s
let names (st: Students) =
Set.map (fun s -> st[s].name)
let classmateNames c s =
classmates c s >> names students
let katiMates = classmateNames "ITT8060" kati.id
f >> g applies f, then g:
(f >> g) x = g (f x)
Who shares a course with a student? Take the course's set, remove the student, turn ids into names: a pipeline of small functions.
katiMates waits for the model. What is katiMates enrolments?
set ["Liis"; "Rein"]
ITT8060// Enrolments = Map<Course, Set<StudentId>>
let enrolled (c: Course) (e: Enrolments) =
match Map.tryFind c e with
| Some ss -> ss
| None -> Set.empty
let enrol (c: Course) (s: StudentId)
(e: Enrolments) =
Map.add c (Set.add s (enrolled c e)) e
type Enrolments2 = Set<Course * StudentId>
let enrolments2: Enrolments2 =
set [ "ITT8060", kati.id; "ITT8060", rein.id
"ITT8060", liis.id; "ITI0210", rein.id
"ITI0210", liis.id ]
let enrolled2 c (e: Enrolments2) =
Set.filter (fun (c', _) -> c' = c) e
|> Set.map (fun (_, s) -> s)
let enrol2 c s (e: Enrolments2) = Set.add (c, s) e
Concept 1's fourth scenario: a map to sets, or a set of pairs. Same
questions on the other type — what is enrolled2 "ITI0210" enrolments2?
set [StudentId 2; StudentId 3]
Same answer — but enrol2 is one call and enrolled2 a scan over every
pair: the type decided which operation is a lookup and which is a search.
ITT8060type Country = string
type GMap = Set<Country * Country>
type Color = Set<Country>
type Coloring = Set<Color>
let exampleMap: GMap =
Set.ofList [("a", "b"); ("c", "d"); ("d", "a")]
A map is a set of neighbouring pairs; a colouring a set of sets of countries — no country coloured twice, by type.
Recursion over a set as over a list: Set.minElement cols is the head,
Set.remove col cols the tail, the empty set the base case.
colMap exampleMap?
set [set ["a"; "c"]; set ["b"; "d"]]
The whole program: notes, Worked examples, A.
ITT8060
ITT8060Next week: recursive data types.
Reading: Hansen & Rischel, chapter 3 (records) and chapter 5 (sets and maps); as a companion, Wlaschin, Domain Modeling Made Functional, chapters 4 and 5.
The complete programs — map colouring, the cash register three ways, the practice task and the exercises — are in the notes under Worked examples: notes.html.
ITT8060