Re:Where Did Combinators Come From? Hunting the Story of Mos...
by dvt on Tuesday December 08, 2020 @04:01PM
(#25351299)
Hint: if you pass it x, y, z where they are functions, it won't complain :) Python is (weakly) typed, so in your case, it tries to call 1(something) and that won't work. In (untyped) Lambda Calculus, everything is a function.
[ Parent
| on HN ]
Re:Where Did Combinators Come From? Hunting the Story of Mos...
by mikewarot on Tuesday December 08, 2020 @06:38PM
(#25353149)
I got S(I)(I)(I)(4) to return 4, everything else is errors.
Re:Where Did Combinators Come From? Hunting the Story of Mos...
by carapace on Tuesday December 08, 2020 @08:25PM
(#25354182)
> TypeError: 'int' object is not callable
Heh, yeah I did that too when I was wrapping my head around 'em. The thing to remember is that in SKI combinator logic nothing else exists. You can't pass 'int' objects to SKI because they don't exist in that universe. (Obviously you can in the sense that Python will let you do it, but it's logically meaningless.)
(I had to break out pencil and paper and walk through some evaluations of SKI expressions by hand.)
Let's have versions of I that also print their names and arg as a side effect:
def E(x):
print('E', x)
return x
def F(x):
print('F', x)
return x
def G(x):
print('G', x)
return x
Consider: S(E)(F)
Start with the definition of S: S = lambda x: lambda y: lambda z: x(z)(y(z))
Substitute: S = lambda E: lambda y: lambda z: E(z)(y(z))
So now S returns this new function (call it S') that has the E function
embedded in the closure: S' = lambda y: lambda z: E(z)(y(z))
That gets called on F: S'(F)
Substitute: S' = lambda F: lambda z: E(z)(F(z))
And this creates S'' with both E and F embedded: S'' = lambda z: E(z)(F(z))
So the result is a function that takes some function z, calls F on it,
calls G on it, and then calls the result of the first on the result of
the second. S''(G)
E(G)(F(G))
E(G) -> G ; prints "E <function G at 0x000001E0A4A741F0>"
G(F(G))
F(G) -> G ; prints "F <function G at 0x000001E0A4A741F0>"
G(G)
prints "G <function G at 0x000001E0A4A741F0>"
G <--- G returns itself.
In action: In [16]: S(E)(F)
Out[16]: <function __main__.<lambda>.<locals>.<lambda>.<locals>.<lambda>(z)>
In [17]: S(E)(F)(G)
E <function G at 0x000001E0A4A741F0>
F <function G at 0x000001E0A4A741F0>
G <function G at 0x000001E0A4A741F0>
Out[17]: <function __main__.G(x)>
Since G is also a version of I we can get away with passing it anything
(not just functions): In [18]: S(E)(F)(G)("Hi!")
E <function G at 0x000001E0A4A741F0>
F <function G at 0x000001E0A4A741F0>
G <function G at 0x000001E0A4A741F0>
G Hi!
Out[18]: 'Hi!'
That's why you can pass anything to SIII (aka S(I)(I)(I) in Python) and it returns it, because SIII = I. In [19]: S(I)(I)(I) is I
Out[19]: True
HTH