Processing math: 100%

05 April 2012

Fixed-Point Magic: Successive Recursive Calls

A brief summary of the Y-combinator trick, and an unusual use.

You may be familiar with the Y combinator and its sexy uses in functional programming. The standard examples of use are logging and caching. Let's summarize how the trick works. Given a recursive function f:ab, you rewrite it into a non-recursive function f:(ab)(ab): The body of ff (that is, f applied to its first argument which we name f) looks exactly the same as the body of f. At this point, types are already long, so I'll write t for tt. For example the type of f is (ab). The Y combinator has the type αβ,(αβ)(αβ). In words, Y takes a function like f and ‘ties the knot’ to give us back f. Before tying the knot, however, you may apply to f any number of transformers, which are functions of type (ab). Roughly, each transformer pre-processes the argument and post-processes the result.

Here is an example.

let rec y f x = f (y f) x
let fib' fib n = if n < 2 then n else fib (n - 1) + fib (n - 2)
let p_int x = printf "%d\n" x
let log p f' f n = p n; f' f n
let fib = y (log p_int fib')

The call fib 3 prints the following:

3
1
2
0
1

But suppose you want to print the call graph.

init -> 3
3 -> 1
3 -> 2
2 -> 0
2 -> 1

To do so, a transformer needs access to the current value of the argument, but also to the previous value. We will use a combinator successive of type αβγ,(αγ)(αβ)(γ×αβ) It transforms an untied function of type (ab) into an untied function of type (c×ab), using a helper of type ac.

let successive g f' f (x, y) = f' (fun z -> f (g y, z)) y
let p_string_int (s, n) = printf "%s -> %d\n" s n
let fib = y (log p_string_int (successive string_of_int fib'))
let _ = fib ("init", 3)

Let's break down the types involved in the second to last line, because they can get long and may be hard to track in one's head.

fib' : (int -> int) -> (int -> int)
string_of_int : int -> string
successive :
  (int -> string)
  -> ((int -> int) -> (int -> int))
  -> (string * int -> int) -> (string * int -> int)
successive string_of_int fib' :
  (string * int -> int) -> (string * int -> int)
p_string_int : string * int -> unit
log :
  (string * int -> unit)
  -> ((string * int -> int) -> (string * int -> int))
  -> (string * int -> int) -> (string * int -> int)
log p_string_int (successive string_of_int fib') :
  (string * int -> int) -> (string * int -> int)
y :
  ((string * int -> int) -> (string * int -> int))
  -> (string * int -> int)
fib : string * int -> int

No comments:

Post a Comment

Note: (1) You need to have third-party cookies enabled in order to comment on Blogger. (2) Better to copy your comment before hitting publish/preview. Blogger sometimes eats comments on the first try, but the second works. Crazy Blogger.