2008-02-26 Typing the Y Combinator ======================================================================== >>> Typing the Y Combinator Typing the Y combinator is always a tricky issue. For example, in standard ML you must write a new type definition to do this: datatype 'a t = T of 'a t -> 'a val y = fn f => (fn (T x) => (f (fn a => x (T x) a))) (T (fn (T x) => (f (fn a => x (T x) a)))) In OCaml, you can turn on recrsive types, and it will infer the correct type: # let y f = (fun x -> x x) (fun x -> fun z -> f (x x) z);; val y : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = # let fact = y (fun fact n -> if n<1 then 1 else n* fact(n-1)) ;; val fact : int -> int = # fact 5;; - : int = 120 It is also possible to write this expression in typed-scheme, but we will need to write a type definition as well. First of all, the type of Y should be straightforward: it is a fixpoint operation, so it takes a `T -> T' function and produces its fixpoint. The fixpoint itself is some `T' (such that applying the function on it results in itself). So this gives us: (: make-recursive : ((T -> T) -> T)) However, in our case `make-recursive' computes a *functional* fixpoint, for unary `S -> T' functions, so we should narrow down the type (: make-recursive : (((S -> T) -> (S -> T)) -> (S -> T))) Now, in the body of `make-recursive' we need to add a type for the `x' arugment which are behaving in a weird way: they are both a function and its own argument. (Remember -- I will say the next sentence twice: "I will say the next sentence twice".) We need a recursive type definition for that: (define-type (Tau S T) = (Rec this (this -> (S -> T)))) This type is tailored for our use of `x': given a type `T', `x' is a function that will consume *itself* (hence the `Rec') and spit out the value that the `f' argument consumes -- a `T -> T' function. The resulting full version of the code: (: make-recursive : (All (S T) (((S -> T) -> (S -> T)) -> (S -> T)))) (define (make-recursive f) (define-type (Tau S T) = (Rec this (this -> (S -> T)))) ((lambda: ([x : (Tau S T)]) (f (lambda: ([z : S]) ((x x) z)))) (lambda: ([x : (Tau S T)]) (f (lambda: ([z : S]) ((x x) z)))))) (: fact : (Number -> Number)) (define fact (make-recursive (lambda: ([fact : (Number -> Number)]) (lambda: ([n : Number]) (if (zero? n) 1 (* n (fact (- n 1)))))))) (fact 120) ========================================================================