• Home
  • Textbooks
  • Programming Languages: Build, Prove, and Compare
  • User-defined, algebraic types (and pattern matching)

Programming Languages: Build, Prove, and Compare

Norman Ramsey

Chapter 8

User-defined, algebraic types (and pattern matching) - all with Video Answers

Educators


Chapter Questions

03:46

Problem 1

Identify matching values. Test your understanding of pattern matching by seeing which values are matched by the pattern (PAIR (cons $x$ (cons y zs)) w). For each of the following expressions, tell whether the pattern matches the value denoted. If the pattern matches, say what value is bound to each of the four variables $x, y, z s$, and $w$. If it does not match, explain why not.
505a. ?sample expressions 505a) $\equiv$
(PAIR (PAIR 'Fisher 105) '(llll)
(PAIR '(\#t \#f) 314159)
(PAIR '(a) '( $(b$ c d ) )
(PAIR '(a b) ' $(c$ d))
(PAIR ' (a b c $\left.)^{\prime}(d)\right)$

Anh Nguyen
Anh Nguyen
Numerade Educator
08:24

Problem 2

Match a list of pairs of integers. Define a function consecutive-pair that takes a list of integers and returns, as a pair, the first two consecutive integers in the given list that are also consecutive in the list of all integers. If there is no such pair, consecutive-pair should return NONE. Use only two patterns: the wildcard pattern and the nested pattern (cons $n$ (cons $\mathrm{m} \mathrm{ms}$ )).
505b. $\langle$ exercise transcripts 505b? $\equiv$
$505 c b$
$\rightarrow$ consecutive-pair
<function> : ((list int) $\rightarrow$ (option (pair int int)))
NONE : (option (pair int int))
$\rightarrow$ (consecutive-pair ' ( $\left.\begin{array}{lll}7 & 8 & 1\end{array}\right)$ )
(SOME (PAIR 78 )) : (option (pair int int))
$\rightarrow$ (consecutive-pair '( $\left(\begin{array}{llll}4 & 3 & 3 & 4\end{array}\right)$ )
(SOME (PAIR 34 )) : (option (pair int int))

Tarandeep Singh
Tarandeep Singh
Numerade Educator
03:08

Problem 3

Create a list of pairs. Define a function zip that takes a pair of lists (of equal length) and returns the list of pairs containing the same elements in the same order. If the lengths don't match, pass the symbol 'length-mismatch to the error primitive. Do not use if, null?, car, or cdr.
505c. ?exercise transcripts 505b $\rangle \equiv$
$\triangleleft 505 \mathrm{~b} 506 \mathrm{a} \triangleright$
$\rightarrow$ zip
<function> : (forall ['a 'b] ((list 'a) (list 'b) $\rightarrow$ (list (pair 'a 'b))))
$\rightarrow(z i p$ '(a b c) '( 1 ( 2 a 3$))$
((PAIR a 1) (PAIR b 2) (PAIR c 3)) : (list (pair sym int))
$\rightarrow(z i p$ ' (a b c) '())
Run-time error: length-mismatch

Morgan Cheatham
Morgan Cheatham
Numerade Educator

Problem 4

Nested patterns. If your zip from Exercise 3 includes more than one case expression, reimplement it using a single case expression, while still adhering to all the restrictions in Exercise 3 .

Check back soon!
00:31

Problem 5

Using option values. Define a function List. find which generalizes function exists? by producing a witness (if one exists). Given a predicate $p$ ? and a list $\mathrm{xs}$, List.find returns (SOME $v$ ) if $\mathrm{xs}$ contains a value $v$ satisfying $p$ ?, and NONE otherwise.
506a. $\langle$ exercise transcripts 505b $\rangle+\equiv$
$4505 \mathrm{c} 506 \mathrm{~b} D$
$\rightarrow$ List.find
<function> : (forall ['a] ('a $\rightarrow$ bool) (list 'a) $\rightarrow$ (option 'a)))
$\rightarrow$ (define positive? (n) $(>\mathrm{n} 0)$ )
$\rightarrow$ (List.find positive? '( $\left.\left(\begin{array}{lllllll}-3 & -2 & -1 & 0 & 1 & 2 & 3\end{array}\right)\right)$
(SOME 1) : (option int)
$\rightarrow$ (List.find (lambda $\left.(n)(>n \quad 100))^{\prime}\left(\begin{array}{lllllll}-3 & -2 & -1 & 0 & 1 & 2 & 3\end{array}\right)\right)$
NONE : (option int)

Trinity Steen
Trinity Steen
Numerade Educator

Problem 6

Consuming option values. A partial function from $\tau$ to $\tau^{\prime}$ can be represented by a total function of type $\tau \rightarrow \tau^{\prime}$ option. Such a function can be mapped over a list, keeping only the SOME results. In this example I map the consecutive-pair function from Exercise 2.
506b. $\langle$ exercise transcripts 505b $\rangle+\equiv$
$4506 \mathrm{a} 506 \mathrm{c} D$
$\rightarrow$ map-partial
?function> : (forall ['a 'b] (('a $\rightarrow$ (option 'b)) (list 'a) $\rightarrow($ list 'b)))
'( $\left(\begin{array}{lllll}7 & 7 & 4 & 2\end{array}\right)$ ' ( $\left.\left.\begin{array}{llll}4 & 3 & 3 & 4\end{array}\right)\right)$ )
((SOME (PAIR 1 2)) (SOME (PAIR 45 )) NONE (SOME (PAIR 9 1)) ...
$\rightarrow$ (map-partial consecutive-pair (list6 '( $\left(\begin{array}{lll}1 & 2 & 3\end{array}\right)$ '( $(4 \quad 56)$
((PAIR 1 2) (PAIR 45 ) (PAIR 0 1) (PAIR 3 4)) : (list (pair int int))
Without using if, null?, car, or cdr, implement map-partial.

Check back soon!
01:06

Problem 7

Removing option values. Define function keep-somes, which takes a list of option values and returns only the SOME values, with SOME stripped off.
506c. $\langle$ exercise transcripts 505b $\rangle+\equiv$
$\triangleleft 506 \mathrm{~b} 506 \mathrm{~d} \triangleright$
$\rightarrow$ keep-somes
<function> : (forall ['a] ((list (option 'a)) $\rightarrow$ (list 'a)))
$\rightarrow$ (keep-somes
(list6 NONE (SOME 'freedom) NONE NONE (SOME 'is) (SOME 'slavery)))
(freedom is slavery) : (list sym)

Bahar Tehranipoor
Bahar Tehranipoor
Numerade Educator
02:11

Problem 8

Maps over option. Define function Option. map, which acts like map but works on option values, not list values.
506d. $\langle$ exercise transcripts $505 b\rangle+\equiv$
$\triangleleft 506 \mathrm{c} 507 \mathrm{a} \triangleright$
$\rightarrow$ Option.map
<function> : (forall ['a 'b] (('a $\rightarrow$ 'b) (option 'a) $\rightarrow$ (option 'b)))
$\rightarrow$ (Option.map positive? NONE)
NONE : (option bool)
$\rightarrow$ (Option.map positive? (SOME 4))
(SOME \#t) : (option bool)
$\rightarrow$ (Option.map reverse NONE)
NONE : (forall ['a] (option (list 'a)))
$\rightarrow$ (Option.map reverse (SOME '( $\left.\left.\begin{array}{lll}1 & 2 & 3\end{array}\right)\right)$ )

Srilakshmi E K
Srilakshmi E K
Numerade Educator
01:06

Problem 9

Nested option. Define function Option.join, which takes an "option of option" and returns a single option that is SOME whenever possible:
507a. $\langle$ exercise transcripts 505b?+
$\triangleleft 506 \mathrm{~d} 507 \mathrm{~b} D$
$\rightarrow$ Option.join
<function> : (forall ['a] ((option (option 'a)) $\rightarrow$ (option 'a)))
$\rightarrow$ (Option.join (SOME NONE))
NONE : (forall ['a] (option 'a))
$\rightarrow$ (Option.join (SOME (SOME 4)))
(SOME 4) : (option int)

Bahar Tehranipoor
Bahar Tehranipoor
Numerade Educator
09:34

Problem 10

Comparison using order. Define a higher-order sort function mk-sort of type (forall ['a] ('a 'a $\rightarrow$ order) $\rightarrow(($ list 'a) $\rightarrow($ list 'a)))). Avoid using if, null?, car, and cdr.
507b. exercise transcripts 505b $+\equiv$
$\triangleleft 507 \mathrm{a} 507 \mathrm{c} D$
$\rightarrow m k-s o r t$
<function> : (forall ['a] ('a 'a $\rightarrow$ order) $\rightarrow($ (list 'a) $\rightarrow($ ist 'a))))
$\rightarrow$ (mk-sort Int.compare)
<function> : ((list int) $\rightarrow$ (list int))
$\rightarrow\left(\right.$ it ' $\left.\left(\begin{array}{lllll}0 & 2 & 1 & 5\end{array}\right)\right)$
( $\left(\begin{array}{llll}1 & 2 & 5 & 5\end{array}\right):$ (list int)
$\rightarrow($ val sort-down (mk-sort (lambda $(n \mathrm{~m})$ (Int.compare $m n)))$ )
$\rightarrow$ (sort-down ' $\left.\left(\begin{array}{lllll}0 & 2 & 1 & 5 & 5\end{array}\right)\right)$
$\left(\begin{array}{llll}5 & 2 & 1 & 0\end{array}\right):$ (list int)

Raphael Tinoco
Raphael Tinoco
Numerade Educator
03:08

Problem 11

Merge sort via pattern matching. Without using if, nu11?, car, or cdr, define a higher-order mergesort function, which should have the same type as above: (forall ['a] (('a'a $\rightarrow$ order $) \rightarrow(($ list 'a) $\rightarrow($ list'a $)))$ ). I recommend defining a top-level mergesort that takes compare as an argument, and which contains a letrec that defines split, merge, and sort.

Unlike most sorting algorithms, mergesort requires two base cases: not only is a list matching '() considered sorted, but so is a list matching (cons $x^{\prime}()$ ). Your sort function should therefore discriminate among three cases-the two base cases and one inductive case. The inductive case splits the list into two smaller lists, sorts each, and merges the results.

Morgan Cheatham
Morgan Cheatham
Numerade Educator

Problem 12

Higher-order functions with order. Comparison is useful not just on individual values but on pairs, triples, lists, and so on. Such comparisons are typically lexicographic: you compare the first elements, and if they are unequal, that's the result of the comparison. But if the first elements are equal, you compare the remaining elements lexicographically. Try your hand at the higher-order functions below. Use case expressions, and look for opportunities to use the wildcard pattern.
(a) Without using if, define function compare-like-pairs, which is given a comparison function for values of type $\tau$ and returns a comparison function for values of type $\tau \times \tau$.
507c. $\langle$ exercise transcripts 505b $\rangle+\equiv$
$\triangleleft 507 \mathrm{~b} 508 \mathrm{a} \triangleright$
$\rightarrow$ (check-type compare-like-pairs
(forall ['a] (('a 'a $\rightarrow$ order) $\rightarrow$
((pair 'a 'a) (pair 'a 'a) $\rightarrow$ order $)))$ )
Your implementation of compare-like-pairs should pass these tests:
508a. ?exercise transcripts 505b)+?
$4507 \mathrm{c} 508 \mathrm{~b} \triangleright$
$\rightarrow$ (val compare-NxN (compare-like-pairs Int.compare))
$\rightarrow$ (check-expect (compare-NxN (pair 1 2) (pair 3 4)) LESS)
$\rightarrow$ (check-expect (compare-NxN (pair 7 2) (pair 3 4)) GREATER)
$\rightarrow$ (check-expect (compare-NxN (pair 3 2) (pair 3 4)) LESS)
$\rightarrow$ (check-expect (compare-NxN (pair 3 4) (pair 3 4)) EQUAL)
$\rightarrow$ (check-expect (compare-NxN (pair 5) (pair 4)) GREATER)
(b) Define function compare-pairs, which is given two comparison functions, one for values of type $\tau$ and one for values of type $\tau^{\prime}$, and returns a comparison function for values of type $\tau \times \tau^{\prime}$.
508b. ?exercise transcripts $505 b\rangle+\equiv$
$\triangleleft 508 \mathrm{a} 508 \mathrm{c} \triangleright$
$\rightarrow$ (check-type compare-pairs
(forall ['a 'b] ('a 'a $\rightarrow$ order) ('b 'b $\rightarrow$ order) $\rightarrow$
((pair 'a 'b) (pair'a 'b) $\rightarrow$ order $))$ ))
(c) Use compare-pairs to implement compare-like-pairs.
(d) Define function compare-lists, which is given a comparison function for values of type $\tau$ and returns a comparison function for values of type $\tau$ list. Lists should be ordered lexicographically (dictionary order), and the empty list should be considered smaller than any nonempty list. The function should pass these tests:
508c. $\langle$ exercise transcripts $505 b\rangle+\equiv$
$\triangleleft 508 \mathrm{~b} 514 \mathrm{a} \triangleright$
$\rightarrow$ (val compare-Ns (compare-lists Int.compare))
$\rightarrow$ (check-expect (compare-Ns '((2 155 5) '(2 115 5)) EQUAL)
$\rightarrow$ (check-expect (compare-Ns '(2 115 5 ) '(2 1)) GREATER)
(e) Use compare-pairs to implement compare-lists without matching a value of type order directly.

Check back soon!
04:09

Problem 13

Implement a function bt-depth that gives the depth of a binary tree from Section 8.1 (page 463). An empty tree h?s depth zeró; a nonempty tree has depth 1 more than the maximum depth of its subtrees.

Nick Johnson
Nick Johnson
Numerade Educator

Problem 14

A binary search tree can represent a finite map, which is to say a set of key-value pairs in which each key appears at most once. A binary search tree is one of the following:
$\bullet$ The empty tree, which represents the empty set of key-value pairs
$\bullet$ An internal node, which has a key, a value, a left subtree, and a right subtree, and which represents the singleton set containing the given key and value, unioned with the sets represented by the left and right subtrees
Every binary search tree satisfies an order invariant. The empty tree satisfies the order invariant by definition. An internal node satisfies the order invariant if it has all of these properties:
$\bullet$ The left subtree satisfies the order invariant.
$\bullet$ The right subtree satisfies the order invariant.
$\bullet$ Every key in the left subtree is smaller than the key in the internal node.
$\bullet$ Every key in the right subtree is greater than the key in the internal node.

Using an algebraic data type, implement a binary search tree. Use pattern matching, not if. Ideally your tree will be polymorphic in both keys and values, but if it simplifies things sufficiently, you could use integer keys.
(a) Define an algebraic data type that represents binary search trees. I recommend that your algebraic data type include a value constructor that takes no arguments and that represents an empty tree.
(b) Define an insert function that takes a key, a value, and a tree, and returns a new tree that is like the original tree, but binding the given key to the given value. (In particular, if the given key is present in the old tree, the new tree should associate that key with the new value.)
(c) Define a lookup function that takes a key and a tree. If the tree associates the key with a value $v$, the function should return (SOME $v$ ). If not, the function should return NONE.
(d) Define a delete function that takes a tree and a key, and returns a tree that is equivalent to the original, except that the key is not associated with any value. If you are not familiar with deletion in binary search trees, the usual heuristic is to reduce every case to the problem of deleting the key-value pair from a given internal node. This problem divides into three overlapping cases:
$\bullet$ If the left subtree is empty, the internal node can be replaced by the right subtree.
$\bullet$ If the right subtree is empty, the internal node can be replaced by the left subtree.
$\bullet$ If neither subtree is empty, delete the largest key-value pair from the left subtree. Form a new internal node using that key-value pair along with the modified left subtree and the original right subtree.
(e) Define a record type that holds insert, lookup, and delete functions. Define a polymorphic function that takes a compare function and returns such a record.
(f) Define a function treefoldr that does an inorder traversal of a binary search tree. For example, given tree $t$,
(treefoldr (lambda (key value answer) (cons key answer)) '() t) will return a sorted list of the keys in the tree.

Check back soon!

Problem 15

List append takes time and space proportional to the length of the left-hand argument. In this problem, you define a new representation of sequences that supports append in constant time.
Informally, a sequence of $\tau$ 's is either empty, or it is a single value of type $\tau$, or it is a sequence of $\tau$ 's followed by another sequence of $\tau$ 's.
(a) Use this informal definition to define an algebraic datatype seq of kind $(* \Rightarrow *)$. As with any abstraction that is defined by choices, your definition should contain one value constructor for each choice.
(b) Define s-cons, with type (forall ['a] ('a (seq 'a) $\rightarrow$ (seq 'a))), to add a single element to the front of a sequence, using constant time and space.
(c) Define s-snoc, with type (forall ['a] ('a (seq'a) $\rightarrow$ (seq'a))), to add a single element to the back of a sequence, using constant time and space. (As always, snoc is cons spelled backward.)
Here the order of arguments is the opposite of the order in which the results go in the data structure. That is, in (s-snoc $x \mathrm{xs}$ ), $x$ follows $x s$. The arguments are the way they are so that s-snoc can be used with foldl and foldr.
(d) Define s-append, type (forall ['a] ((seq'a) (seq'a) $\rightarrow$ (seq'a))), to append two sequences, using constant time and space.
(e) Define list-of-seq, with type (forall ['a] ((seq 'a) $\rightarrow$ (list'a))), to convert a sequence into a list containing the same elements in the same order. Function list-of-seq should allocate only as much space as is needed to hold the result.
(f) Without using explicit recursion, define function seq-of-list, with type (forall ['a] ((list'a) $\rightarrow$ (seq'a))), which converts an ordinary list to a sequence containing the same elements in the same order.
(g) Ideally, function list-of-seq would take time proportional to the number of elements in the sequence. But when a sequence contains many empty-sequence constructors, it can take longer. Prevent this outcome by altering your solutions to maintain the invariant that the empty-sequence value constructor never follows or is followed by another sequence.

Check back soon!
01:28

Problem 16

When lists are immutable, as they are in $\mu$ Scheme and in the ML family, they appear not to support typical imperative operations, like inserting or deleting a node at a point. But these operations can be implemented on purely functional data structures, efficiently, in ways that look imperative. The implementation uses a technique called the zipper.
You will learn the zipper by implementing a list with indicator. A list with indicator is a nonempty sequence of values, together with an "indicator" that points at one value in the sequence. Elements can be inserted or deleted at the indicator, in constant time.
(a) Define a representation for type ilist of kind (* $\Rightarrow *$ ). Document your representation by saying, in a short comment, what sequence is meant by any value of type 'a ilist.
Given a good representation, the code is easy: almost every function can be implemented as a case expression with one or two choices, each of which has a simple right-hand side. But a good representation might be challenging to design.
(b) Support the documentation in part (a) by writing list-of-ilist:
510. $\langle$ list-with-indicator functions 510$\rangle \equiv$
511a
(check-type list-of-ilist
$($ forall ['a] ((ilist'a) $\rightarrow($ list'a))))
(c) Define function singleton-ilist, which takes a single value and returns a list whose indicator points at that value.
511a. $\langle$ list-with-indicator functions 510$\rangle+=$
$4510511 b D$
(check-type singleton-ilist (forall ['a] ('a $\rightarrow$ (ilist 'a))))
(d) Define function at-indicator, which returns the value the indicator points at.
511b. ?list-with-indicator functions 510$\rangle+\equiv$
$4511 \mathrm{a} 511 \mathrm{c} b$
(check-type at-indicator (forall ['a] ((ilist 'a) $\rightarrow$ 'a)))
(e) To move the indicator, define indicator-left and indicator-right, with these types:
511c. $\langle$ list-with-indicator functions 510$\rangle+\equiv$
$\triangleleft 511 \mathrm{~b} 511 \mathrm{~d} \triangleright$
(check-type indicator-left
(forall ['a] ((ilist 'a) $\rightarrow$ (option (ilist 'a)))))
(check-type indicator-right
$($ forall ['a] ((ilist 'a) $\rightarrow$ (option (ilist 'a)))))
Calling (indicator-leftxs) creates a new list ys that is like xs, except the indicator is moved one position to the left. And it returns (SOME ys). But if the indicator belonging to $x$ s already points to the leftmost position, then (indicator-left xs) returns NONE. Function indicator-right is similar. Both functions must run in constant time and space.

These functions "move the indicator," but no mutation is involved. Instead of mutating an existing list, each function creates a new list.
(f) To remove an element, define delete-left and delete-right, with these types:
511d. $\langle$ list-with-indicator functions 510$\rangle+\equiv$
$\triangleleft 511 \mathrm{l}$ 511e
(check-type delete-left
(forall ['a] ((ilist 'a) $\rightarrow$ (option (ilist 'a)))))
(check-type delete-right
$($ forall ['a] ((ilist 'a) $\rightarrow$ (option (ilist 'a)))))
Calling (delete-left xs) creates a new list ys that is like xs, except the element to the left of the indicator has been removed. And it returns (SOME ys). If the indicator points to the leftmost position, then delete-left returns NONE. Function delete-right is similar. Both functions must run in constant time and space, and as before, no mutation is involved.
(g) To insert an element, define insert-left and insert-right, with these types:
511e. $\langle$ list-with-indicator functions 510$\rangle+\equiv$
$4511 \mathrm{~d} 512 \mathrm{a} \triangleright$
(check-type insert-left
(forall ['a] ('a (ilist 'a) $\rightarrow$ (ilist 'a))))
(check-type insert-right
(forall ['a] ('a (ilist 'a) $\rightarrow$ (ilist 'a))))
Calling (insert-left $x \times s$ ) returns a new list that is like $x s$, except the value $x$ is inserted to the left of the indicator. Function insert-right is similar. Both functions must run in constant time and space. As before, no mutation is involved.
(h) Define functions ffoldl and ffoldr, with these types:
512a. $\langle$ list-with-indicator functions 510$\rangle+\equiv$
$4511 \mathrm{e}$
(check-type ffold 1
(forall ['a 'b] (('a 'b $\rightarrow$ 'b) 'b (ilist 'a) $\rightarrow$ 'b)))
(check-type ffoldr
(forall ['a 'b] ('a 'b $\rightarrow$ 'b) 'b (ilist 'a) $\rightarrow$ 'b)))
These functions do the same thing as foldl and foldr, but on lists with indicators. They ignore the position of the indicator.

To test these functions, start with the list test-ilist defined below. The list is created by a sequence of insertions, plus a movement. To emphasize the imperative feel of the abstraction, test-ilist is created by using let* to rebind the name $x$ s repeatedly. The code should remind you of a sequence of assignment statements.
512b. $\langle$ list-with-indicator test cases $512 b\rangle \equiv$
$512 \mathrm{c} b$
(val test-ilist
(let* ((xs (singleton-ilist 3))
(xs (insert-left 1 xs))
(xs (insert-left $2 x s$ ))
(xs (insert-right $4 x s$ ))
(xs (case (indicator-right $x s)$ ((SOME ys) ys)))
(xs (insert-right 5 xs)) )
xs))
The resulting test-ilist should be the list '(1 2345 ), with the indicator pointing at 4. It should pass these tests:
512c. (list-with-indicator test cases $512 b\rangle+\equiv$
$4512 b$
(check-expect (at-indicator test-ilist) 4)
(check-expect (Option.map list-of-ilist (delete-left test-ilist))

Grigoriy Sereda
Grigoriy Sereda
Numerade Educator

Problem 17

Tries as sets. A binary trie searches by looking at bits of an integer. A binary trie can represent a set of integers, and set union, intersection, and difference are easy to implement efficiently. You'll implement a little-endian binary trie, which looks at the least-significant bits first.
A binary trie has three kinds of nodes: empty, singleton, and union.
$\bullet$ The empty trie represents the empty set of integers.
$\bullet$ The singleton trie contains a single integer $n$, and it represents the singleton set $\{n\}$.
$\bullet$ The union trie contains two subtries $e$ and $o$ (for "even" and "odd"), which represent sets $\llbracket e \rrbracket$ and $\llbracket o \rrbracket$, respectively. In the little-endian form, the union trie represents the set
$$
\{2 \cdot k \mid k \in \llbracket e \rrbracket\} \cup\{2 \cdot k+1 \mid k \in \llbracket o \rrbracket\}
$$
where $2 \cdot k$ means twice $k$. As a consequence of this definition, when you are looking for an element $n$ in a union trie, you look in $e$ if $n$ is even and in $o$ if $n$ is odd-and either way, in the sub-trie you look for $n \operatorname{div} 2$.
Implement a little-endian binary trie as follows:
(a) Define an algebraic data type ints that represents a binary trie, which in turn represents a set of integers.
(b) Define function ints-member?, which tells whether an integer is in the set.
(c) Define function ints-insert, which inserts an integer into the set.
(d) Define function ints-delete, which removes an integer from the set, if present.
(e) Define function ints-union, which takes the union of two sets. Find an implementation that allocates fewer CONVALs than simply union by repeated insertion.
(f) Define function ints-inter, which takes the intersection of two sets.
(g) Define function ints-diff, which takes the intersection of two sets.
(h) Define function ints-fold, which folds over all the integers in a trie. Give it type (forall ['a] ((int 'a $\rightarrow$ 'a) 'a ints $\rightarrow$ 'a)).
As a representation of sets, the binary trie has some redundancy:
$\bullet$ The empty set can be represented not only as an empty trie, but also as the union of two empty tries-or the union of any two tries that each represent the empty set. The empty set is represented most efficiently as the empty trie.
$\bullet$ A singleton set can be represented not only as a singleton trie, but also as the union of a singleton trie and an empty trie-or the union of any two tries that respectively represent the appropriate singleton set and the empty set. A singleton set is represented most efficiently as a singleton trie.

The potential redundancy can be eliminated by wrapping the value constructor for union in a "smart constructor":
(i) Revisit the functions you have implemented above, and identify which functions can create a union node of which one child is empty and the other is either empty or singleton.
(j) Rewrite the functions you have implemented above so that no function ever creates a union node of which one child is empty and the other is either empty or singleton. The standard technique is not to use the value constructor for union directly, but to define a smart constructor: A smart constructor is a function that, at the abstract level, has the same specification as a value constructor, but at the representation level, can may be more efficient. For the binary trie, the smart constructor should recognize two special cases: union of empty and empty should return empty, and union of singleton $n$ and empty should return either singleton $2 \cdot n$ or singleton $2 \cdot n+1$. In other cases, the smart constructor should behave like the value constructor for a union trie.

Check back soon!

Problem 18

Generalize your results from Exercise 17 to implement a finite map with integer keys.
(a) To represent a finite map with integer keys, define an algebraic data type intmap of kind $(*=*)$.
(b) Define values and functions with these types:
514a. exercise transcripts 505b)+?
$4508 \mathrm{c} 514 \mathrm{~b} \triangleright$
$\rightarrow$ (check-type empty-intmap
(forall ['a] (intmap 'a)))
$\rightarrow$ (check-type intmap-insert
(forall ['a] (int 'a (intmap 'a) $\rightarrow$ (intmap 'a))))
$\rightarrow$ (check-type intmap-find
(forall ['a] (int (intmap 'a) $\rightarrow$ (option 'a))))
$\rightarrow$ (check-type intmap-delete
(forall ['a] (int (intmap 'a) $\rightarrow$ (intmap 'a))))
$\rightarrow$ (check-type intmap-fold
(forall ['a 'b]
$(($ int 'a 'b $\rightarrow$ 'b) 'b (intmap 'a) $\rightarrow$ 'b $)))$

Check back soon!

Problem 19

A ternary search tree combines aspects of a trie and of a binary search tree. The original ternary search tree is specialized to null-terminated C strings (Bentley and Sedgewick 1997, 1998). The ternary search tree that you will implement is polymorphic: it works with any type of list.

A tree of type (ternary $\tau_a \tau_b$ ) represents a finite map whose keys have type (list $\tau_a$ ) and whose values have type $\tau_b$. A ternary search tree has two species of nodes: a decision node and a final node.
- A decision node contains a decision element $d$ of type 'a, and it has three children, each of which is also a ternary search tree:
- The left subtree stores all key-value pairs whose keys are lists that begin with an element that is smaller than $d$.
- The right subtree stores all key-value pairs whose keys are lists that begin with an element that is larger than $d$.
- The middle subtree stores all the key-value pairs whose keys have the form (cons $d x s$ ). However, in the middle subtree, only the $x s$ part of the key is used-the $d$ part is implicit in the position of the middle subtree directly under a decision node.

In addition to its three subtrees, the decision node also stores the value whose key is the empty list (if any).
- A final node stores only the value whose key is the empty list, if any. It has no children.
This exercise has three parts:
(a) Using an algebraic data type, define a representation of ternary search trees.
(b) Define function ternary-insert, to insert a key-value pair into a ternary search tree.
(c) Define function ternary-find, to look up a key in a ternary search tree.
Your functions should be higher-order and should have these types:
514b. $\langle$ exercise transcripts 505b $\rangle+\equiv$
$\triangleleft 514 \mathrm{a} 522 \mathrm{~b} \triangleright$
$\rightarrow$ (check-type ternary-find
(forall ['a 'b] (('a 'a $\rightarrow$ order) $\rightarrow$
((list 'a) (ternary 'a 'b) $\rightarrow$ (option 'b)))))
$\rightarrow$ (check-type ternary-insert
(forall ['a 'b]
$($ ('a 'a $\rightarrow$ order $) \rightarrow$
$(($ list 'a) 'b (ternary 'a 'b) $\rightarrow($ ternary 'a 'b $)))$ ))

Check back soon!
01:01

Problem 20

Using equational reasoning, prove two laws about function preorder-elems, which is defined on page 463 .
$\begin{aligned} &(\text { preorder-elems BTEMPTY })=' \mathrm{C}) \\ &(\text { preorder-elems }(\text { BTNODE } \times \text { t1 t2) })=(\text { cons } x(\text { append }(\text { preorder-elems t1 }) \\ &(\text { preorder-elems t2 })))\end{aligned}$
(preorder-elems BTEMPTY) $=$ ' $($ )
(preorder-elems (BTNODE $\times$ t1 t2)) $=$ (cons $\times$ (append (preorder-elems t1)
(preorder-elems t2)))

Raj Bala
Raj Bala
Numerade Educator

Problem 21

In all functional languages, one of the most important compiler optimizations is function inlining. Inlining sometimes results in expressions that have a "case-of-case" structure:
$$
\begin{aligned}
& \text { (case } \\
& \text { (case } \left.e\left[\begin{array}{ll}
p_1 & e_1
\end{array}\right] \cdots\left[\begin{array}{ll}
p_k & e_k
\end{array}\right]\right) \\
& {\left[\begin{array}{ll}
p_1^{\prime} & e_1^{\prime}
\end{array}\right]} \\
& \left.\left[\begin{array}{ll}
p_m^{\prime} & e_m^{\prime}
\end{array}\right]\right) \text {. } \\
&
\end{aligned}
$$
Write an algebraic law that will enable a compiler to rewrite such an expression so that the scrutinee is not a case expression. Assume that all the expressions are evaluated without side effects, so duplicating code is OK. (With luck, any duplicate code will be eliminated by applications of laws described in Section 8.3.)

Check back soon!
01:02

Problem 22

Using define* as described in Section 8.4 (page 481), rewrite the implementation of preorder-elems to make the laws in Exercise 20 blindingly obvious.

Sherrie Fenner
Sherrie Fenner
Numerade Educator
01:39

Problem 23

Using syntactic sugar, extend $\mu \mathrm{ML}$ 's lambda expression so that each formal parameter is a pattern, not just a variable (Section 8.4, page 480 ).
$\bullet$ In the exptable function of Appendix S, change the lambda case to use parser patFormals instead of formals; patFormals parses a list of patterns in brackets.
$\bullet$ Redefine function lambda to rewrite a list of patterns as a list of variables, adding a case expression. If the list of patterns is empty, the LAMBDA takes no arguments, and it need not be transformed. Otherwise, where lambda takes one or more patterns as arguments, implement the desugaring with the help of functions freshVars to get new names for the arguments, tupleexp to form a scrutinee, and tuplepat to build the single pattern that goes in the case expression.

Even with this extension, many lambda expressions use only PVAR patterns. These lambda expressions can be transformed using case, but if you instead convert them to ordinary lambda expressions without case, your code will be easier to debug.

Dominador Tan
Dominador Tan
Numerade Educator

Problem 24

Using syntactic sugar, extend $\mu \mathrm{ML}$ 's define form so the formal-parameter list is a list of patterns, not just a list of variables (Section 8.4, page 480). Replace the formals parser with patFormals, and rewrite the define function. As in Exercise 23, use freshVars, tupleexp, and tuplepat-if you've done Exercise 23, you may be able to reuse code. And as in Exercise 23, if all the patterns are PVAR patterns, it's better not to introduce a case expression.

Check back soon!

Problem 25

Using syntactic sugar, implement the lambda* expression (Section 8.4, page 482). Don't change any parsers; just update the ML code in Appendix S to replace the lambdastar function with one that returns OK $e$, where $e$ is a lambda expression. As above, use freshVars, tupleexp, and tuplepat to good advantage. And your lambdastar function should check that each list of patterns has the same length (shown as $n$ on page 482). The check isn't strictly necessary, but if it is omitted and the lengths aren't equal, the error messages are baffling.

Check back soon!

Problem 26

Using syntactic sugar, implement clausal definitions. That is, implement the define* form (Section 8.4, page 481). Don't change any parsers; just update the ML code in Appendix S to replace the definestar function with one that returns $0 K d$, where $d$ is a definition. If you have done Exercise 25, use ML function lambdastar to implement definestar.

Check back soon!

Problem 27

Using syntactic sugar, implement the transformation that enables let expressions to bind patterns, not just variables (Section 8.4, page 482). Rewrite the letx expression-builder function in Appendix S, which should now take a (pat * exp) list instead of a (name * exp) list. Your new letx function must handle three cases:
$\bullet$ If the parser is configured correctly, a LETREC uses only PVAR patterns, and it can be converted to an ordinary LETREC. If your LETREC case encounters a pattern that is not a PVAR, your interpreter should halt with a fatal error message.
$\bullet$ A LETSTAR should be desugared into nested LETS in the standard way.
$\bullet$ A LET should be desugared as shown on page 482. The desugaring must be applied to each pattern/expression pair.
You will also need to replace these parsers in the exptable function:
$$
\begin{array}{ll}
\hline \text { Original } \mu M L & \mu M L \text { with patterns everywhere } \\
\hline \text { letBs } & \text { patBs } \\
\text { letstarBs } & \text { patBs } \\
\text { letrecBs } & \text { patLetrecBs } \\
\hline
\end{array}
$$

Check back soon!

Problem 28

Using syntactic sugar, extend val so it can with the usage string "(val pat e)" and parser valpat $\langle \$\rangle$ pattern $\langle *\rangle$ exp. The valpat function is a new definition builder that you need to write. It should build one val binding for each free variable of the given pattern; free variables can be found using function freePatVars. Once built, the bindings are combined with a binding to it, and the list of bindings is wrapped in the secret DEFS constructor.

Perform the full desugaring only if the pattern in the val is in CONPAT form. If the pattern is a variable or a wildcard, turn it into a variable and move on.

Check back soon!

Problem 29

In $C$, a struct definition is generative. A struct definition is any occurrence of struct that is followed by a list of fields in curly braces. For example, the definition
517a. $\langle$ generativity.c 517a) $\equiv$
$517 \mathrm{~b} D$
typedef struct point $*$ Point;
does not include a struct definition, because no fields are given. But the declaration
517b. $\langle$ generativity.c $517 \mathrm{a}\rangle+\equiv$
$\triangleleft 517 \mathrm{a}$
double distance_from_origin(struct point \{double $x$; double $y$; $\}$ );
does include a struct definition, because fields are given-even though those fields are in unusual places.

This exercise will help you discover how generativity works in $\mathrm{C}$ and how your favorite $\mathrm{C}$ compiler deals with it.
(a) Write a C program that contains at least two distinct struct definitions, both of struct point, and both containing two double fields $x$ and $y$.
(b) Extend your C program so that you try to use one struct point where the other one is expected, causing a compile-time error.
(c) Find a C compiler that complains about your program of part (b), saying that it expected struct point but found struct point instead-or something similar.

Check back soon!

Problem 30

In most statically typed languages, generativity is associated with a particular syntactic form: in the ML family, the definition of an algebraic data type; in $\mathrm{C}$, the definition of a struct or union. Such languages give programmers no choices: if you define a particular species of type, the definition is generative.

Modula-3, by contrast, lets you choose which types are generative (Cardelli et al. 1992). No type is generative by default, but any pointer type can be made generative by branding it, using the BRANDED keyword. (Pointer types are called "references," and the corresponding type constructor is written REF.)
(a) Consult the language definition for Modula-3-try not to distract yourself with subtyping-and design a similar branded mechanism for $\mu \mathrm{ML}$. Update the formal system to account for brands, and give additional rules for the translation of type syntax into types.
(b) Write rules for type equivalence that account for brands.
(c) Write introduction and elimination rules for expressions that have branded types. This part of the exercise may suggest to you why the designers of Modula- 3 limited branding to reference types.

Check back soon!
00:53

Problem 31

Show that $\mu$ ML's kind system is a conservative extension of Typed $\mu$ Scheme's kind system. Start with a definition of erasure:
$\bullet$ The erasure of the empty environment is the empty environment.
$\bullet$ If $\hat{\Delta}$ is the erasure of environment $\Delta$, then the erasure of environment $\Delta^{\prime}=\Delta\{T \mapsto(\tau, \kappa)\}$ is $\hat{\Delta}\{T \mapsto \kappa\}$.
$\bullet$ The erasure of judgment $\Delta \vdash t \sim \tau:: \kappa$ is judgment $\Delta \vdash \tau:: \kappa$.
Prove both of the following claims:
(a) If there is a derivation of $\Delta \vdash t \sim \tau:: \kappa$, then there is a derivation of $\hat{\Delta} \vdash \tau:: \kappa$.
(b) If there is a derivation of $\Delta_0 \vdash \tau:: \kappa$, then there exist a $t$ and a $\Delta$ such that $\Delta_0=\hat{\Delta}$ and there is a derivation of $\Delta \vdash t \sim \tau:: \kappa$.

Amrita Bhasin
Amrita Bhasin
Numerade Educator

Problem 32

As your programs get more sophisticated and types get bigger, you will wish for type abbreviations. In this exercise, you get them. You will change the rules and the code for the type translation on page S455.
(a) Change environment $\Delta$ so that a type name $T$ may stand for one of two things:
$\bullet$ A type
$\bullet$ A function from a list of types to a type
(b) To keep things simple, change the KINDAPP rule so that only a type name $T$ can be applied. The rule will then work as long as $T$ stands for a type.
(c) When $T$ stands for the function $\lambda \alpha_1, \ldots, \alpha_n . \tau$, use this new rule:
$$
\begin{aligned}
& \Delta(t)=\left(\lambda \alpha_1, \ldots, \alpha_n \cdot \tau, *_1 \times \cdots \times *_n \Rightarrow \kappa\right) \\
& \frac{\Delta \vdash t_i \leadsto \tau_i:: \kappa_i, 1 \leq i \leq n}{\Delta \vdash\left(t t_1 \cdots t_n\right) \sim \tau\left[\alpha_1 \mapsto \tau_1, \ldots, \alpha_n \mapsto \tau_n\right]:: \kappa} . \\
&
\end{aligned}
$$
(KINDAPPABBREV)
(d) Implement the new semantics in function txtype.
(e) Add a new definition form (type $\left[\left[\alpha_1 \cdots \alpha_n\right]\right] T$ t), which enters $T$ into $\Delta$ as the appropriate function on types.

Check back soon!
05:49

Problem 33

A pattern is a variable, a wildcard, or a constructor application. Except for the wildcard, each of these syntactic elements can also be used to form an expression. Put patterns into one-to-one correspondence with a subset of expressions and show that this correspondence preserves types:
(a) For each form of pattern $p$ except the wildcard, show the corresponding expression form $\llbracket p \rrbracket$. For clarity, you may wish to express the correspondence as an ML function of type (pat $\rightarrow$ exp).
(b) Use metatheory to prove that if $p$ has no wildcard, and if $\Gamma, \Gamma^{\prime} \vdash p: \tau$, then $\Gamma+\Gamma^{\prime} \vdash \llbracket p \rrbracket: \tau$
(c) Extend the expression syntax with a new form HOLE, written in the concrete syntax as _. A hole behaves like a name with type scheme $\forall \alpha . \alpha$.
$$
\text { HOLE } \frac{\alpha \notin \mathrm{ftv}(\Gamma)}{\Gamma \vdash \text { HOLE }: \tau} \quad \frac{\alpha}{\mathrm{T}, \Gamma \vdash \text { HOLE }: \alpha} \text { HOLE, With CONStraints }
$$
(d) Given the extension in part (c), use metatheory to prove that regardless of whether $p$ has a wildcard, if $\Gamma, \Gamma^{\prime} \vdash p: \tau$, then $\Gamma+\Gamma^{\prime} \vdash \llbracket p \rrbracket: \tau$.

Chris Trentman
Chris Trentman
Numerade Educator

Problem 34

Using the operational semantics, prove that if there is a derivation of the judgment $\langle p, v\rangle \mapsto \dagger$, then the derivation contains an instance of either the FAILVCON rule or the FAILBAREVCON rule.

Check back soon!

Problem 35

The constraint-based rules for typechecking patterns (Figure 8.9, page 498) do more bookkeeping than they have to. The constraint can be eliminated: instead of judgment $C, \Gamma, \Gamma^{\prime} \vdash p: \tau$, new rules can use the simpler judgment $\Gamma, \Gamma^{\prime} \vdash p: \tau$. Constraint $C$ is eliminated by finding a substitution $\theta$ that solves $C$ and whose domain is the free variables of $C$. The original derivation is transformed by applying $\theta$ to it. The resulting judgment about $p$ is $\theta C, \Gamma, \theta \Gamma^{\prime} \vdash p: \theta \tau$, and since $\theta C=\mathbf{T}$ by definition, this transformation leads to the simpler judgment form.

The hard part of the exercise is to show that applying $\theta$ doesn't affect the type that is inferred in the rest of the derivation.
(a) Prove that in any derivation of the judgment $\mathbf{T}, \Gamma \vdash K: \tau$, the free variables of $\tau$ are distinct from the free variables of $\Gamma$.
(b) By induction over derivations of judgments of the form $C, \Gamma, \Gamma^{\prime} \vdash p: \tau$, prove that the free type variables of $C$ are distinct from the free type variables of $\Gamma$.
(c) By induction over derivations of judgments of the form $C, \Gamma, \Gamma^{\prime} \vdash p: \tau$, prove that the free type variables of $\Gamma^{\prime}$ are distinct from the free type variables of $\Gamma$.
(d) Prove that in a valid derivation of $C, \Gamma \vdash e: \tau$, if there is a subderivation of $C^{\prime}, \Gamma, \Gamma^{\prime} \vdash p: \tau^{\prime}$, and if $\theta$ is a substitution whose domain is contained in $\mathrm{fv}\left(C^{\prime}\right)$, and if $\theta C^{\prime} \equiv \mathbf{T}$, then
i. Substitution $\theta$ can affect only the types of the new bindings $\Gamma^{\prime}$ and the type $\tau^{\prime}$ of pattern $p$.
ii. Applying $\theta$ to the entire derivation results in a valid derivation.
iii. Applying $\theta$ does not change $\tau$, that is, $\theta \tau=\tau$.
The preceding results imply that constraint $C$ can be eliminated by solving it eagerly at each application of the PATVCON rule.
(e) Write new rules for the judgment form $\Gamma, \Gamma^{\prime} \vdash p: \tau$. Except for choice of fresh type variables, your rules should be deterministic. Rules PATBAREVCON, PATWILDCARD, and PATVAR can be rewritten simply by removing the trivial output constraint. The PATVCON rule needs to solve constraint $C$ and apply the resulting substitution to $\Gamma^{\prime}$ and $\alpha$. (Constraints $C_1, \ldots, C_m$ are all trivial, and therefore so is constraint $C^{\prime}$.)
(f) Simplify the CHOICE rule.
(g) Rewrite the code to reflect your new, simpler rules.

Check back soon!
07:08

Problem 36

Extend pattern matching so it is possible to match on an integer literal. An integer-literal pattern has type int, introduces no bindings, and matches only the corresponding integer value.
(a) Extend the parser, type checker, and evaluator of $\mu \mathrm{ML}$ to support integer-literal patterns.
(b) Using patterns, write a recursive Fibonacci function that does not use if.

Chris Trentman
Chris Trentman
Numerade Educator
04:04

Problem 37

Extend pattern matching so it is possible to match a quoted symbol. A quoted-symbol pattern has type sym, introduces no bindings, and matches only the corresponding symbol. Extend the parser, type checker, and evaluator of $\mu \mathrm{ML}$ to support quoted-symbol patterns.

Chris Trentman
Chris Trentman
Numerade Educator
01:28

Problem 38

Section 8.9 .2 on page 499 describes checks for exhaustive and redundant patterns. Given a scrutinee of type $\tau$, a set of patterns is exhaustive if every value that inhabits type $\tau$ is matched by some pattern. In this exercise you write an analysis that prints a warning message when it finds a non-exhaustive or redundant pattern match.

The idea is this: Suppose you have the set of all possible values of type $\tau$. And suppose that, for each member of this set, you can tell if it does or does not match a particular pattern $p$. Then you can check for both exhaustiveness and redundancy using an iterative algorithm that maintains a set of all values not yet matched.
($\bullet$ The set is initially the set of all values of type $\tau$.
($\bullet$ At each step of the iteration, the set is split into two subsets: those that do and do not match pattern $p$.
($\bullet$ The set that matches $p$ must not be empty; otherwise pattern $p$ is redundant.
($\bullet$ The set that does not match $p$ is passed on to the next pattern.
($\bullet$ At the end of the iteration, if the set of values not yet matched is nonempty, the pattern match is not exhaustive.

There's only one snag: for interesting types, the sets are infinite. To represent such sets, I define a "simple value set" to be one of two choices:
($\bullet$ All values of type $\tau$, for a given $\tau$
- A given value constructor $K$, applied to a list of zero or more simple value sets

A full set of values is a collection of simple sets, using the collections defined in Section H.2.4 (page S218).
520. $\langle$ exhaustiveness analysis for $\mu M L 520\rangle \equiv$
(S420b) $521 \mathrm{~b} D$
datatype simple_vset $=$ ALL of ty
I ONE of vcon* simple_vset list
type vset $=$ simple_vset collection
In the analysis, each set of values is classified according to whether it does or does not match a pattern. Full sets can be classified, simple sets can be classified, and even lists of sets can be classified. Start with this code:
Classification takes a single value set as input and produces a collection as output. A simple value set $v$ is classified by a pattern $p$ as follows:

($\bullet$ If $p$ is a wildcard or a variable, it always matches, and the result is a singleton collection containing (true, vs).
($\bullet$ If $p$ is ( $K p_1 \cdots p_n$ ) and $v s$ is the application of a different constructor $K^{\prime}$, then $v$ doesn't match, and the result is a singleton collection containing (false, vs).
($\bullet$ If $p$ is ( $\left.K p_1 \cdots p_n\right)$ and $v$ is (K $v_1 \cdots v_n$ ), then the value-set list $v_1 \cdots v_n$ is classified against the pattern list $p_1 \cdots p_n$. Do this using a third function, classifyList. The empty value-set list always matches the empty pattern list, and the subset of $v_1 \cdots v_n$ matching $p_1 \cdots p_n$ is the subset of $v_1$ matching $p_1$ combined with the subset of $v_2 \cdots v_n$ matching $p_2 \cdots p_n$. To implement classifyList, use recursion, use classifySimple, and use the collection function map2C.
($\bullet$ Finally, if $p$ is ( $K p_1 \cdots p_n$ ) and $v$ is ALL $\tau$, change ALL $\tau$ to unroll $\tau$ and try again. When $\tau$ is an algebraic data type, unroll $\tau$ returns a vset that is equivalent to $\mathrm{ALL} \tau$ but that does not use ALL at top level.
Function call vcons 0 mu returns the name and type scheme of each value constructor associated with mu. Function vcons 0 is omitted from this book.

Using this classification, the exhaustiveness check can be implemented in three stages, as described on the next page.
Implement the exhaustiveness check in three stages:
(a) Implement classifySimple, but leave out the case of matching value constructors.
(b) Implement classifyList, which has type pat list * simple_vset list $\rightarrow$ (bool * simple_vset list) collection, and use it to complete the implementation of classifySimple. Classify lists using these rules:
($\bullet$ The empty list of values always matches the empty list of patterns.
($\bullet$ A list of values $v:: v s$ matches a list of patterns $p:: p s$ if and only if $v$ matches $p$ and also $v s$ matches $p s$.
(c) Implement exhaustivenessCheck, replacing this placeholder:
The replacement should compute successive value sets that are as yet unmatched. Start with the value-set collection singleC (ALL tau), and classify value sets using each pattern from ps in turn. Issue a warning message for each redundant pattern, and if the list of patterns is not exhaustive, issue a warning for that too.

Grigoriy Sereda
Grigoriy Sereda
Numerade Educator