Combinatory logic from scratch

Posted on Tue 04 April 2017 in blog

Cause it's sooooo sexy, let's speak about Combinatory Logic!

  • Rule 1: You don't talk about Combinatory Logic
  • Rule 2: You don't talk about Combinatory Logic
  • Rule 3: Combinatory Logic is based on Lambda Calculus (see Wikipedia for both)
  • Rule 4: A combinator is a Lambda expression taking One and only One combinator as parameter, and returning a Combinator.

As i'm speaking to developers, I'LL use the C# Lambda syntax which is: (parameter) => statement Let's now try our first Combinator, named the Identity Combinator I = (a) => a; I named it I, it takes one parameter, localy named a and return the parameter as is.

Important point: How to build combinator taking more than one parameter ? In C# you should use (a, b, c) => blah blah... but the Rule 4 forbid us to give more than one paraneter, so let's cheat, imagine : K = (x) => (y) => x; K is a combinator taking x, returning a combinator taking y and returning x. so we have K(x) = (y) => x and K(x)(y) = x! So K take two arguments, x and y, and returns x, but! K can take only one argument, look at the K(x) = (y) => x ...

Let's try with three arguments:

S = (x) => (y) => (z) => x(z)(y(z))

can be called with one,

S(x) returns (y) => (z) => x(z)(y(z))

two,

S(x)(y) returns (z) => x(z)(y(z))

or three arguments:

s(x)(y)(z) returns x(z)(y(z))

In combinatory logic, they write:

  • I a = a
  • K x y = x
  • S x y z = x(z)(y(z))

then they say that in fact, I can be build from S and K:

I = SKK

Ok but what does that means?

Where are arguments? it's easy: I = S(K)(K); S can take 2 parameters S(x)(y) returns (z) => x(z)(y(z)), so: I = (z) => K(z)(K(z)) We have to execute it from left to right, remember, K(a)(b) returns a, so (with a == z and b == K(z)): I = (z) => z;

Do you want more?

Let's try to understand

B = S (K S) K x y z

B stands for Barbara, from "Syllogism Barbara" (Wikipedia explains:

  • All men are animals.
  • All animals are mortal. -All men are mortal.

So before all, write B as we understand it, and for readability reasons, currently executed combinator and it's arguments will be emphased:

  • B = S (K(S)) K (x) (y) (z)

We have to execute it from left to right, and we have a S with three parameters: S(a)(b)(c) returns a(c)(b(c)):

  • B = K (S) (x) (K(x)) (y) (z)

From left to right we have a K with two parameters, S and x, it will return S:

  • B = S (K(x)) (y) (z)

Calling S with three parameters (K(x)), (y), and(z)returns(K(x))(z)((y)(z))`:

  • B = K (x) (z) ((y)(z))

Calling K with two parameters (x), and (z), it returns x:

  • B = x((y)(z))

Which can be simplified to:

  • B = x(y(z))

It's time to try it !

delegate C C(C c);
static void Main(string[] args)
{
    C K = (a) => (b) => a;
    C S = (a) => (b) => (c) => a(c)(b(c));
    C I = S(K)(K);
    C B = S(K(S))(K);
}

It works! Enjoy!! Next time, we will try a Swap combinator, a Combinator reducing to himself and progressing step to the Y Combinator! dramatic chord