I'm trying to grok the combinators, so I wrote the basic ones: K
and S
in Typescript:
const K: Kestrel = a => b => a;
const S: Starling = a => b => c => a(c)(b(c));
Then, I followed the Wikipedia article about SKI Boolean Logic:
TRUE = K
FALSE = SK
therefore:
const TRUE: True = K;
const FALSE = S(K);
And this is passing the basic tests:
const t = () => true;
const f = () => false;
test('Ktf = (TRUE)tf = t', () => {
const actual = TRUE(t)(f)();
expect(actual).toBe(K(t)(f)());
expect(actual).toBe(true);
});
test('SKxy = y', () => {
expect(S(K)(t)(f)()).toBe(false);
expect(FALSE(t)(f)()).toBe(false);
});
So far so good, but when it comes to NOT
Wikipedia says:
NOT = (FALSE)(TRUE) = (SK)(K)
(TRUE)NOT = TRUE(FALSE)(TRUE) = FALSE
(FALSE)NOT = FALSE(FALSE)(TRUE) = TRUE
I wrote a code and the tests and I got:
const NOT = S(K)(K);
test('(TRUE)NOT = FALSE', () => {
expect(TRUE(NOT)(t)(f)()).toBe(false); // <- see these () after (t)(f)?
expect(TRUE(NOT)(f)(t)()).toBe(true);
});
test('(FALSE)NOT = TRUE', () => {
expect(FALSE(NOT)(t)(f)).toBe(true); // <- here we don't have these ()
expect(FALSE(NOT)(f)(t)).toBe(false);
});
My question is about the two lines that I highlighted with comments: why to make the tests pass, I need this asymmetry between TRUE
and FALSE
? Seems like I followed all the details in K
and S
implementation, but in the case of TRUE(NOT)(t)(f)
I get a function () => false
, and in the case of FALSE(NOT)(t)(f)
I get a false
.
Is there any detail I missed or Wikipedia is not precise enough in definitions?
P.S.: Here is the code: https://repl.it/repls/FuchsiaFuchsiaTruetype
Two things, 1. your implementation of NOT
is wrong, and 2. the asymmetry is purely a coincidence.
Note that both TRUE
and FALSE
are curry functions that are waiting for two more args to evaluate.
Now check this fact:
FALSE(NOT)(t) === t
FALSE
effectively doesn't care about 1st arg, and all it does is to simply return the 2nd arg. So you get t
as return value, and when you do FALSE(NOT)(t)(f)
, you're effectively calling t(f)
where f
is useless arg, and it yields true
. Thus the coincidence.
NOT
Wikipedia page writes NOT as postfix operator, but that's just a literal expression they use to write down the idea. In JS code you cannot write the same.
(T)NOT = T(F)(T) = F
See that NOT is expanded as two args to be passed to the function before it. To implement this idea in JS, you need to write:
const NOT = b => b(S(K))(K);
and reverse the calling signature back to normal form: NOT(TRUE)
.
I modified your original code here: https://repl.it/@hackape/KnowledgeableWealthyDowngrade