1

I managed to implement Church encoding and Y-Combinator using ES6 arrow function in javascript. But when I tried to evaluate the factorial function,

FALSE = a => b => b 
TRUE = a => b => a 

ZERO = f => z => z
ONE = f => z => f(z)
SIX = f => z => f(f(f(f(f(f(z))))))
isZERO = n => n(x => FALSE)(TRUE)
SUCC = n => f => z => f(n(f)(z))
MULT = n => m => f => z => n(m(f))(z)

PAIR = a => b => z => z(a)(b)
FIRST = p => p(a => b => a)
SECOND = p => p(a => b => b)
ZZ = PAIR(ZERO)(ZERO)
SS = p => PAIR(SECOND(p))(SUCC(SECOND(p)))
PRED = n => FIRST(n(SS)(ZZ))

FactGen = fact => n =>
  isZERO(n)
    (ONE)
    (MULT(n)(fact(PRED(n))))

Y = g => (x => g(y => x(x)(y))) (x => g(y => x(x)(y)))

Y(FactGen)(SIX) (x=>x+1)(0)

I got 'Uncaught RangeError: Maximum call stack size exceeded(…)' error.

If I change FactGen,

FactGen = fact => n => n == 0 ? 1 : n * fact(n - 1)
Y(FactGen)(6)
720

It just works.

What I want to know is Church numeral version of it. How can I achieve this?

1 Answers1

3

Your problem is that JavaScript is not lazy evaluated. Specifically, the isZero "if" does evaluate all of its arguments before it can check whether the first is zero.

We can fix that using an if with unit functions:

// type Bool = a -> a -> a
// type Lazy a = () -> a
// IF :: Bool -> Lazy a -> Lazy a -> a
IF = c => a => b => c(a)(b)()

FactGen = fact => n =>
  IF(isZERO(n))
    (()=>ONE)
    (()=>MULT(n)(fact(PRED(n))))
//   ^^^^

or omit the IF wrapper and change your boolean encodings directly to

// type Bool = Lazy a -> Lazy a -> a
FALSE = a => b => b()
TRUE = a => b => a()
Bergi
  • 513,640
  • 108
  • 821
  • 1,164