Chapter 4Standard ML

Higher-Order Functions

Functions that take or return functions

Concept

A higher-order function (Def 1.23, slides p.209) is one that takes a function as an argument or returns a function as its result. Functions in SML are first-class values — they can be stored, passed, and returned just like ints or strings.

Examples of taking functions as arguments:
- map applies a function to every element of a list.
- filter keeps only elements satisfying a predicate.

Examples of returning functions:
- fn x => fn y => x + y takes x and returns a function that adds x to its argument. (This is currying.)
- fn k => fn _ => k takes k and returns a constant function.

Composition: the central idea is that we build new behavior by plugging functions together. map f is a function; filter p is a function; map f (filter p xs) chains them. This is the functional analogue of pipes.

Example — define map ourselves:

fun mymap (f, nil) = nil | mymap (f, h :: t) = (f h) :: mymap (f, t);

Test: mymap (fn x => x + 1, [10, 20, 30]) = [11, 21, 31].

Animation — sml higher order
Transcript — click a line to jump9 cues
  1. 0.0sHigher-order functions: map
  2. 1.4sInput row shows cells 1, 2, 3
  3. 3.1sAn amber function box slides in: fn x implies x times 2
  4. 4.9sOutput row label appears, empty for now
  5. 6.0sBox slides over 1, output cell 2 drops in
  6. 7.7sBox slides over 2, output cell 4 drops in
  7. 9.4sBox slides over 3, output cell 6 drops in
  8. 11.0sFunction box moves back and fades out
  9. 12.0smap applies a function to every element
Definition 1.26: Cartesian vs Cascaded functions

A function can be written in two equivalent ways:

  • Cartesian form: takes all arguments in one tuple. f(x, y) = x + y.
  • Cascaded form: returns a function expecting the next argument. f x = fn y => x + y.

In SML, the cascaded form is the default since functions are curried. The cascaded form f x y is sugar for (f x) y. The cartesian form needs an explicit tuple: f (x, y).

Bracket elision rules (Def 1.26):
1. Application associates left: f a b c = ((f a) b) c.
2. Arrow -> associates right: a -> b -> c -> d = a -> (b -> (c -> d)).

These two rules together mean fn x => fn y => fn z => x + y + z can be written as the cascaded fn x => fn y => fn z => x + y + z and called as f 1 2 3.

Higher-order list functions: the my* family

The standard higher-order list functions can be re-implemented as my* functions for understanding:

fun myexists p nil = false | myexists p (h::t) = p h orelse myexists p t; fun myforall p nil = true | myforall p (h::t) = p h andalso myforall p t; fun myfilter p nil = nil | myfilter p (h::t) = if p h then h :: myfilter p t else myfilter p t;

Patterns:
- myexists p l returns true if at least one element of l satisfies p.
- myforall p l returns true if every element of l satisfies p.
- myfilter p l returns the sublist of elements satisfying p.

Order of recursion matters: all three traverse left-to-right. myexists short-circuits on the first true (good); myforall short-circuits on the first false (good); myfilter cannot short-circuit (must visit all elements to find the ones that pass).

Used in assignments 4.4 and 4.5.
- Assignment 4.4: define divisible d n (uses mod), then nextPrime n, then primes n (uses myexists).
- Assignment 4.5: implement myfilter, myexists, myforall.

Cascaded application. myexists (fn x => x > 5) [3, 7, 2]:
- p(3)=(3>5)=falsep(3) = (3 > 5) = \text{false}. Continue.
- p(7)=(7>5)=truep(7) = (7 > 5) = \text{true}. Return true. ✓

List.nth and List.length: standard SML list operations

SML's standard basis library provides List.nth and List.length. They can be re-implemented:

fun nth 0 (h::_) = h | nth n (_::t) = nth (n - 1) t | nth _ nil = raise Subscript; fun len nil = 0 | len (_::t) = 1 + len t;

List.nth n l returns the nn-th element of ll (0-indexed). Raises Subscript if nlength(l)n \geq \text{length}(l).

List.length is identical to our length (built-in in the playground as length).

Used in assignment 4.8 for permutations: nth i perm accesses the i-th element of a permutation.

Lemma 6.3: Relations as functions

Lemma 6.3. A binary relation RA×BR \subseteq A \times B can be viewed as a function R:AP(B)R: A \to \mathcal{P}(B) defined by R(a):={bB(a,b)R}R(a) := \{b \in B \mid (a,b) \in R\}.

Proof. The definition R(a):={bB(a,b)R}R(a) := \{b \in B \mid (a,b) \in R\} is well-formed: for each aAa \in A it produces a unique subset of BB. Hence RR is a (total) function AP(B)A \to \mathcal{P}(B). Conversely, given any function f:AP(B)f: A \to \mathcal{P}(B), the relation {(a,b)bf(a)}A×B\{(a, b) \mid b \in f(a)\} \subseteq A \times B reconstructs ff. So relations and AP(B)A \to \mathcal{P}(B) functions are equivalent representations. \square

Use. This view lets us apply functional concepts to relations: composition, identity, inverse.

Worked example
Step 0 of 1
Practice — score 100% to advance
Multiple choice
Q1
Per Def 1.23, a function is higher-order iff…
Q2
What does map (fn x => x * 2) [1, 2, 3] evaluate to?
Q3
What does filter (fn x => x > 0) [~1, 2, ~3, 4] evaluate to?
Q4
What is the central idea of higher-order functions?
Q5
Is fn k => (fn _ => k) higher-order?
Q6
What is the type of map?
Q7
The cascaded function fun f x y = x + y has type:
Q8
The relation \leq on N\mathbb{N} viewed as a function maps each nNn \in \mathbb{N} to:
Q9
myexists with an all-false predicate returns:
Q10
List.nth 2 [10, 20, 30, 40] returns:
Fill in the blank
Q1
Application associates to the ___ in SML.
Loading…