## CSCI 2041

### Higher-order Functions: Continuations

Recall that, say, `append` and `sum_tree` are not tail-recursive

``````let rec append l1 l2 = match l1 with
| [] -> l2
| h::t -> h::(append t l2)``````
``````let rec sum_tree t = match t with
| Empty -> 0
| Node (v,lt,rt) -> v + (sum_tree lt) + (sum_tree rt)``````

# Continuations as tail recursion

``````let append l1 l2 =
let rec app_k l k = match l with
| [] -> k l2
| h::t -> app_k t (fun r -> k (h::r))
in app_k l1 (fun x -> x)``````

Evaluating `append [1;2] [3; 4]`:

`≡ app_k [1;2] id`

`≡ app_k [2] k1 (* = fun r1 -> id (1::r1) *)`

`≡ app_k [] k2 (* = fun r2 -> k1 (2::r2) *)`

`≡ k2 l2`

`≡ k2 [3;4]`

`≡ k1 2::[3,4]`

`≡ id 1::2::[3,4]`

``````let append l1 l2 =
let rec app_k l k = match l with
| [] -> k l2
| h::t -> app_k t (fun r -> k (h::r))
in app_k l1 (fun x -> x)``````

The function `k` that processes the result is called a continuation.

The practice of passing a function to process the result of a recursive call is called continuation passing style (CPS).

Example: write `range` in CPS:

``````let range m n =
let rec range_k m k =
if m >= n then k []
else range_k (m+1) (fun r -> k (m::r))
in range_k m (fun r -> r)``````

Another example:

``let dfs (t : 'a btree) =``
``````  let rec dfs_k tr k = match tr with
| Empty -> k []
| Node (v,lt,rt) ->
dfs_k lt (fun lr -> dfs_k rt
(fun rr -> k (lr @ (v::rr)))) in
dfs_k t (fun x -> x)``````

Compare to:

``````let dfs (t: 'a btree) = match t with
| Empty -> []
| Node (v,lt,rt) -> (dfs lt) @ [v] @ (dfs rt)``````

Exercises: `sum_tree_k`, `preorder_k`

Note: what is the type of `app_k`?

``````let rec app_k l k = match l with
| [] -> k l2
| h::t -> app_k t (fun rr -> k (h::rr))``````

`'a list ->` `('a list -> 'b) ->` `'b`

The “initial continuation” determines the result type.

# Continuations for control flow

Continuations can be used to change control flow and evaluation order:

`````` let f s =
let _ = print_string s in s
let g s =
let _ = print_endline s in s
let backwards = (f "hello") ^ (g " world")``````

Evaluates `(g " world")` first, then `(f "hello")`

``````  let f' s k =
k (let _ = print_string s in s)
let forwards =
f' "hello" (fun s -> s^(g " world"))``````

# Continuations for error (event) handling

Continuations can be useful for dealing with errors:

``````type result =  Fine of string | StackOverflow | StackUnderflow
type token = C of float | PL (* other ops omitted for space *)``````
``````let rpnEval tlist k =
let rec evl tl stk k = match (tl,stk) with
| ([],[f]) -> k (Fine (string_of_float f))
| ([],_) -> k StackOverflow
| ((C f)::t,_) -> evl t (f::stk) k
| (PL::t,x::y::stk') -> evl t (x+.y::stk') k
| (PL::t,_) -> k StackUnderflow
in evl tlist [] k``````
``````let result =
rpnEval tokens (function Fine s -> s
| StackOverflow -> "Too many constants"
| StackUnderflow -> "Add without 2 args")``````

# `cs2041.org`

// reveal.js plugins