Y combinator

From Rosetta Code
Revision as of 14:42, 17 August 2009 by rosettacode>Kevin Reid (omit TI-89 BASIC)
Task
Y combinator
You are encouraged to solve this task according to the task description, using any language you may know.

In strict functional programming and the lambda calculus, functions (lambda expressions) don't have state and are only allowed to refer to arguments of enclosing functions. This rules out the usual definition of a recursive function wherein a function is associated with the state of a variable and this variable's state is used in the body of the function.

The Y combinator is itself a stateless function that, when applied to another stateless function, returns a recursive version of the function. The Y combinator is the simplest of the class of such functions, called fixed-point combinators.

The task is to define the stateless Y combinator and use it to compute factorials and Fibonacci numbers from other stateless functions or lambda expressions.

AppleScript

AppleScript is not terribly "functional" friendly. However, it is capable enough to support the Y combinator.

AppleScript does not have anonymous functions, but it does have anonymous objects. The code below implements the latter with the former (using a handler (i.e. function) named 'funcall' in each anonymous object).

Unfortunately, an anonymous object can only be created in its own statement ('script'...'end script' can not be in an expression). Thus, we have to apply Y to the automatic 'result' variable that holds the value of the previous statement.

The identifier used for Y uses "pipe quoting" to make it obviously distinct from the y used inside the definition. <lang AppleScript>to |Y|(f)

 script x
   to funcall(y)
     script
       to funcall(arg)
         y's funcall(y)'s funcall(arg)
       end funcall
     end script
     f's funcall(result)
   end funcall
 end script
 x's funcall(x)

end |Y|

script

 to funcall(f)
   script
     to funcall(n)
       if n = 0 then return 1
       n * (f's funcall(n - 1))
     end funcall
   end script
 end funcall

end script set fact to |Y|(result)

script

 to funcall(f)
   script
     to funcall(n)
       if n = 0 then return 0
       if n = 1 then return 1
       (f's funcall(n - 2)) + (f's funcall(n - 1))
     end funcall
   end script
 end funcall

end script set fib to |Y|(result)

set facts to {} repeat with i from 0 to 11

 set end of facts to fact's funcall(i)

end repeat

set fibs to {} repeat with i from 0 to 20

 set end of fibs to fib's funcall(i)

end repeat

{facts:facts, fibs:fibs} (* {facts:{1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800},

fibs:{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765}}
  • )</lang>

Common Lisp

<lang lisp> (defun Y (f)

 ((lambda (x) (funcall x x)) 
  (lambda (y) 
    (funcall f (lambda (&rest args) 

(apply (funcall y y) args))))))

(defun fac (f)

 (lambda (n) 
   (if (zerop n) 

1 (* n (funcall f (1- n))))))

(defun fib (f)

 (lambda (n) 
   (case n
     (0 0) 
     (1 1) 
     (otherwise (+ (funcall f (- n 1)) 

(funcall f (- n 2)))))))

CL-USER> (loop for i from 1 to 10 collect (list i (funcall (Y #'fac) i) (funcall (Y #'fib) i)))
((1 1 1) (2 2 1) (3 6 2) (4 24 3) (5 120 5) (6 720 8) (7 5040 13)
(8 40320 21) (9 362880 34) (10 3628800 55))

</lang>

D

This is D 2.0 code.

Current state of the language doesn't allow to make //stateless// Y-combinator, where statless is defined as above.

However here are some proposals, how to make Y-combinator in D (though it it statically typed language), and what are the problems with creating //stateless// one.

The easies way to create Y-combinator looks probably like this:

<lang D> import std.stdio;

alias ulong delegate(ulong) delSigRec1; alias delSigRec1 delegate(delSigRec1) delSigAlmost; alias delSigRec1 delegate(delSigAlmost, void*) delSigPf;

void main() {

   delSigRec1 almostFac(delSigRec1 f) {
       return delegate ulong(ulong n) {
           return (n == 0 || n == 1) ? 1Lu : n * f(n-1);
       };
   };
   delSigRec1 almostFib(delSigRec1 f) {
       return delegate ulong(ulong n) {
           return (n == 0 || n == 1) ? 1Lu : f(n-2) + f(n-1);
       };
   };
   auto pf2 = delegate delSigRec1(delSigAlmost almost, void *x) {
       return delegate ulong(ulong n) {
           return ( (almost( (*cast(delSigPf*)x)(almost, x) )) ( n ) );
       };
   };
   delSigRec1 Ycomb(delSigAlmost almost) {
       return pf2(almost, &pf2);
   };
   auto fact = Ycomb(&almostFac);
   foreach (i; 1..15) writef(fact(i), ", "); writefln("");
   auto fibo = Ycomb(&almostFib);
   foreach (i; 1..20) writef(fibo(i), ", "); writefln("");

} </lang>

In D there's no way to create a function or delegate which would take as one of arguments, function or delegate with the same signature. This would probably require some recursive types.

  • Because of that there is casting from void* to delSigPf delegate alias.

Delegates in d consist of a pair of pointers, so the other solution would be to pass pair of pointers, like in example below, but because of usage of a helper function toDel (which converts the two pointers back to delegate) I don't consider this as stateless Y-combinator. <lang D> alias delSigRec1 delegate(void*, void*) delSigPf; // we need to alter this alias

// simplified templates just for this example, can be found // in tango.core.Traits template TypeRet( Fn ) {

   static if( is( Fn Ret == return ) ) alias Ret TypeRet;
   else static assert(false);

}

template TypeParams( Fn ) {

   static if( is( Fn Params == delegate ) ) alias TypeParams!(Params) TypeParams;
   else static if( is( Fn Params == function ) ) alias Params TypeParams;
   else static assert( false );

}

auto toDel(T)(void *a, void *b) {

   T f; 
   f.ptr = a; 
   f.funcptr = cast(TypeRet!(T) function (TypeParams!(T)))b;
   return f;

}

...

   delSigRec1 Ycomb(delSigAlmost almost) {
       return delegate delSigRec1(delSigPf wrap) {
           return wrap(wrap.ptr, wrap.funcptr);
       }( delegate delSigRec1 (void *self_a, void *self_b) {
               return delegate ulong(ulong n) {
                   return ( (almost( 
                       toDel!(delSigPf)(self_a, self_b)(self_a, self_b)
                   )) ( n ) );
               };
           }
       );
   };

</lang>

This seems much better, but as mentioned needs the helper function (or declaration of variable inside the delegate that takes self_a and self_b).


The proper solution should probably look like that: <lang D> alias delSigRec1 delegate(void*) delSigPf; ...

   delSigRec1 Ycomb(delSigAlmost almost) {
       return delegate delSigRec1(delSigPf wrap) {
           return wrap(&wrap);
       }(  delegate delSigRec1(void *x) {
               return delegate ulong(ulong n) {
                   return ( (almost( (*cast(delSigPf*)x)(x) )) ( n ) );
               };
           }
       );
   };

</lang>

This will compile, but will fail when computing values. This happens probably because x inside most inner delegate points to a frame that no longer contains proper data during the call.

E

Translation of: Python

<lang e>def y := fn f { fn x { x(x) }(fn y { f(fn a { y(y)(a) }) }) } def fac := fn f { fn n { if (n<2) {1} else { n*f(n-1) } }} def fib := fn f { fn n { if (n == 0) {0} else if (n == 1) {1} else { f(n-1) + f(n-2) } }}</lang>

<lang e>? pragma.enable("accumulator") ? accum [] for i in 0..!10 { _.with(y(fac)(i)) } [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

? accum [] for i in 0..!10 { _.with(y(fib)(i)) } [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</lang>

Erlang

<lang erlang> Y = fun(M) -> G = fun (F) -> M(fun(A) -> (F(F))(A) end) end,

             G(G)
   end.

Fac = fun (F) ->

         fun (0) -> 1;
             (N) -> N * F(N-1)
         end
     end.

Fib = fun(F) ->

         fun(0) -> 0;
            (1) -> 1;
            (N) -> F(N-1) + F(N-2) 
         end
     end.

(Y(Fac))(5). %% 120 (Y(Fib))(8). %% 21</lang>

Genyris

Translation of: Scheme

<lang python>def fac (f)

   lambda (n)
     if (equal? n 0) 1
       * n (f (- n 1))

def fib (f)

 lambda (n)
   cond
     (equal? n 0) 0
     (equal? n 1) 1
     else (+ (f (- n 1)) (f (- n 2)))

def Y (f)

 (lambda (x) (x x))
     lambda (y)
         f
            lambda (&rest args) (apply (y y) args)

assertEqual ((Y fac) 5) 120 assertEqual ((Y fib) 8) 21</lang>

Haskell

The obvious definition of the Y combinator in Haskell canot be used because it contains an infinite recursive type (a = a -> b). Defining a data type (Mu) allows this recursion to be broken.

<lang haskell>newtype Mu a = Roll { unroll :: Mu a -> a }

fix :: (a -> a) -> a fix = \f -> (\x -> f (unroll x x)) $ Roll (\x -> f (unroll x x))

fac :: Integer -> Integer fac = fix $ \f n -> if (n <= 0) then 1 else n * f (n-1)

fibs :: [Integer] fibs = fix $ \fbs -> 0 : 1 : fix zipP fbs (tail fbs)

 where zipP f (x:xs) (y:ys) = x+y : f xs ys

main = do

 print $ map fac [1 .. 20]
 print $ take 20 fibs</lang>

The usual version using recursion, disallowed by the task:

<lang haskell> fix :: (a -> a) -> a fix f = f (fix f)

fac :: Integer -> Integer fac' f n | n <= 0 = 1

        | otherwise = n * f (n-1)

fac = fix fac'

-- a simple but wasteful exponential time definition: fib :: Integer -> Integer fib' f 0 = 0 fib' f 1 = 1 fib' f n = f (n-1) + f (n-2) fib = fix fib'

-- Or for far more efficiency, compute a lazy infinite list. This is -- a Y-combinator version of: fibs = 0:1:zipWith (+) fibs (tail fibs) fibs :: [Integer] fibs' a = 0:1:(fix zipP a (tail a))

   where
     zipP f (x:xs) (y:ys) = x+y : f xs ys

fibs = fix fibs'

-- This code shows how the functions can be used: main = do

 print $ map fac [1 .. 20]
 print $ map fib [0 .. 19]
 print $ take 20 fibs</lang>

J

In J, functions cannot take functions of the same type as arguments. In other words, verbs cannot take verbs and adverbs or conjunctions cannot take adverbs or conjunctions. However, the Y combinator can be implemented indirectly using, for example, the linear representations of verbs: <lang j>

  sr=. [ 128!:2 ,&< NB. Self referring
  lw=. '(5!:5)<x' (1 :) NB. Linear representation of a word 
  
  Y=. f. (&>)/ lw (&sr) f.
  Y=. 'Y'f. NB. Fixing it
  Y NB. Stateless Y combinator...

((((f.(&>))/)(1 : '(5!:5)<x'))(&([ 128!:2 ,&<)))f.

  u=. [ NB. Function (left)
  n=. ] NB. Argument (right)
  
  fs=. (1:`(n * u sr n - 1:))@.(0: < n) f. NB. Stateless factorial step...
  fs

1:`(] * [ ([ 128!:2 ,&<) ] - 1:)@.(0: < ])

  fs Y NB. Stateless recursive factorial function...

'1:`(] * [ ([ 128!:2 ,&<) ] - 1:)@.(0: < ])&>/'&([ 128!:2 ,&<)

  fs Y 10 

3628800

  Fs=. ((u sr n - 2:) + u sr n - 1:) ^: (1: < n) f. NB. Stateless Fibonacci step...
  Fs

(([ ([ 128!:2 ,&<) ] - 2:) + [ ([ 128!:2 ,&<) ] - 1:)^:(1: < ])

  Fs Y NB. Stateless recursive Fibonacci function...

'(([ ([ 128!:2 ,&<) ] - 2:) + [ ([ 128!:2 ,&<) ] - 1:)^:(1: < ])&>/'&([ 128!:2 ,&<)

  Fs Y 10

55 </lang>

Joy

y   ==  [dup cons]  swap  concat  dup  cons  i

fac == [ [pop null]  [pop succ]  [[dup pred] dip i  *]  ifte ]  y

OCaml

<lang ocaml># type 'a mu = Roll of ('a mu -> 'a)

 let unroll (Roll x) = x
 
 let fix f = (fun x a -> f (unroll x x) a) (Roll (fun x a -> f (unroll x x) a))
 
 let fac f = function
     0 -> 1
   | n -> n * f (n-1)
 
 let fib f = function
     0 -> 0
   | 1 -> 1
   | n -> f (n-1) + f (n-2)

type 'a mu = Roll of ('a mu -> 'a) val unroll : 'a mu -> 'a mu -> 'a = <fun> val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun> val fac : (int -> int) -> int -> int = <fun> val fib : (int -> int) -> int -> int = <fun>

  1. fix fac 5;;

- : int = 120

  1. fix fib 8;;

- : int = 21</lang>

The usual version using recursion, disallowed by the task:

<lang ocaml>let rec fix f x = f (fix f) x</lang>

Perl

<lang perl>my $Y = sub { my ($f) = @_; sub {my ($x) = @_; $x->($x)}->(sub {my ($y) = @_; $f->(sub {$y->($y)->(@_)})})}; my $fac = sub {my ($f) = @_; sub {my ($n) = @_; $n < 2 ? 1 : $n * $f->($n-1)}}; print join(' ', map {$Y->($fac)->($_)} 0..9), "\n"; my $fib = sub {my ($f) = @_; sub {my ($n) = @_; $n == 0 ? 0 : $n == 1 ? 1 : $f->($n-1) + $f->($n-2)}}; print join(' ', map {$Y->($fib)->($_)} 0..9), "\n";</lang>

Python

<lang python>>>> Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))) >>> fac = lambda f: lambda n: (1 if n<2 else n*f(n-1)) >>> [ Y(fac)(i) for i in range(10) ] [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] >>> fib = lambda f: lambda n: 0 if n == 0 else (1 if n == 1 else f(n-1) + f(n-2)) >>> [ Y(fib)(i) for i in range(10) ] [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</lang>

R

<lang R>Y <- function(f) {

 (function(x) { (x)(x) })( function(y) { f( (function(a) {y(y)})(a) ) } )

}</lang>

<lang R>fac <- function(f) {

 function(n) {
   if (n<2)
     1
   else
     n*f(n-1)
 }

}

fib <- function(f) {

 function(n) {
   if (n <= 1)
     n
   else
     f(n-1) + f(n-2)
 }

}</lang>

<lang R>for(i in 1:9) print(Y(fac)(i)) for(i in 1:9) print(Y(fib)(i))</lang>

Ruby

<lang ruby>irb(main):001:0> Y = lambda {|f| lambda {|x| x[x]}[lambda {|y| f[lambda {|*args| y[y][*args]}]}]} => #<Proc:0xb7d3cae0@(irb):1> irb(main):002:0> fac = lambda {|f| lambda {|n| n<2 ? 1 : n*f[n-1]}} => #<Proc:0xb7d2b330@(irb):2> irb(main):003:0> Array.new(10) {|i| Y[fac][i]} => [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] irb(main):004:0> fib = lambda {|f| lambda {|n| n == 0 ? 0 : n == 1 ? 1 : f[n-1] + f[n-2]}} => #<Proc:0xb7d0a1f8@(irb):4> irb(main):005:0> Array.new(10) {|i| Y[fib][i]} => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</lang>

Scheme

Translation of: Python

<lang scheme>> (define (Y f) ((lambda (x) (x x)) (lambda (y) (f (lambda args (apply (y y) args)))))) > (define (fac f) (lambda (n) (if (= n 0) 1 (* n (f (- n 1)))))) > ((Y fac) 5) 120 > (define (fib f) (lambda (n) (cond ((= n 0) 0) ((= n 1) 1) (else (+ (f (- n 1)) (f (- n 2))))))) > ((Y fib) 8) 21</lang>

Slate

The Y combinator is already defined in slate as:

<lang slate> Method traits define: #Y &builder:

 [[| :f | [| :x | f applyWith: (x applyWith: x)]

applyWith: [| :x | f applyWith: (x applyWith: x)]]]. </lang>

Standard ML

<lang sml>- datatype 'a mu = Roll of ('a mu -> 'a)

 fun unroll (Roll x) = x
 
 fun fix f = (fn x => fn a => f (unroll x x) a) (Roll (fn x => fn a => f (unroll x x) a))
 
 fun fac f 0 = 1
   | fac f n = n * f (n-1)
 
 fun fib f 0 = 0
   | fib f 1 = 1
   | fib f n = f (n-1) + f (n-2)

datatype 'a mu = Roll of 'a mu -> 'a val unroll = fn : 'a mu -> 'a mu -> 'a val fix = fn : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b val fac = fn : (int -> int) -> int -> int val fib = fn : (int -> int) -> int -> int - List.tabulate (10, fix fac); val it = [1,1,2,6,24,120,720,5040,40320,362880] : int list - List.tabulate (10, fix fib); val it = [0,1,1,2,3,5,8,13,21,34] : int list</lang>

Tcl

Y combinator is derived in great detail here.

Ursala

The standard y combinator doesn't work in Ursala due to eager evaluation, but an alternative is easily defined as shown <lang Ursala> (r "f") "x" = "f"("f","x") my_fix "h" = r ("f","x"). ("h" r "f") "x"</lang> or by this shorter expression for the same thing in point free form. <lang Ursala> my_fix = //~&R+ ^|H\~&+ ; //~&R</lang> Normally you'd like to define a function recursively by writing f = h(f), where h(f) is just the body of the function with recursive calls to f in it. With a fixed point combinator, you do almost the same thing, except it's f = my_fix "f". h("f"), where the dot represents lambda abstraction and the quotes signify a dummy variable. Using this method, the definition of the factorial function becomes <lang Ursala>

  1. import nat

fact = my_fix "f". ~&?\1! product^/~& "f"+ predecessor</lang> To make it easier, the compiler has a directive to let you install your own fixed point combinator for it to use, which looks like this. <lang Ursala>

  1. fix my_fix</lang>

Having done that, you may write recursive definitions the normal way, as in this definition of the Fibonacci function. <lang Ursala> fib = {0,1}?</1! sum+ fib~~+ predecessor^~/~& predecessor</lang> Note that this way is only syntactic sugar for the for explict way shown above. Without a fixed point combinator given in the #fix directive, this definition of fib would not have compiled. (Ursala allows user defined fixed point combinators because they're good for other things besides functions.) To confirm that all this works, here is a test program applying both of the functions defined above. <lang Ursala>#cast %nLW

examples = (fact* <1,2,3,4,5,6,7,8>,fib* <1,2,3,4,5,6,7,8>)</lang> output:

(
   <1,2,6,24,120,720,5040,40320>,
   <1,2,3,5,8,13,21,34>)