I was confused about what <4> = \lambda ^ 5 4 meant, since it already seemed to have a "4" in it.
The trick is that the 4 seems to be similar to a positional argument index, but numbered inside out.
IOW, in this encoding, <4> is a function that can be called 5 times (the exponent on the lambda) and upon the fifth call will resolve to whatever was passed in 1st (which because of the inside-out ordering is labeled "4").
(For a simpler example, 0 is a function that can be called once and returns its argument.)
So succ is 3-ary; it says, give me a function (index 2, outermost call); next, give me its first argument (index 1, second-outermost call); when you call that (index 0, dropped, innermost call), I'll apply the function to the argument.
But note that if index 2 is a numeral <N>, the outermost call returns a function that will "remember" the next thing passed in and return it after 1 (succ's innermost call) + N + 1 (<N>'s contract) calls.
> Christopher Wadsworth analyzed different properties of numeral systems and the requirements they have to fulfill to be useful for arithmetic.
> Specifically, he calls a numeral system adequate if it allows for a successor (succ) function, predecessor (pred) function, and a zero? function yielding a true (false) encoding when a number is zero (or not).
A numeral system is adequate iff it can be converted to and from Church numerals.
Converting from Church numerals requires functions N0 and Nsucc so that
Church2Num c = c Nsucc N0
while converting to Church numerals requires functions Nzero? and Npred so that
Num2Church n = Nzero? n C0 (Csucc (Num2Church (Npred n)))
with an implicit use of the fixpoint combinator.
An interesting adequate numeral system is what i call the tuple numerals [1], which are simply iterates of the 1-tuple function T = λxλy.y x
So N0 = id, Nsucc = λnλx.n (T x), Npred = λnλx.n x id, and Nzero? = λnλtλf. n (K t) (K f).
These tuple numerals are useful in proving lower bounds on a functional busy beaver [2].
Zero = z. s. z
Succ = n. z. s. s n
isZero = n. n True (_. False)
pred = n. n Zero (r. r)
Addition requires explicit recursion, however (since numbers aren't folds), so I guess you'll have to either use Y combinator or closure-convert manually:
add' = add'. m. n. m n (r. Succ (add' add' r n))
add = add' add'
In any case, arithmetic operations can't be made fully constant-time for obvious reasons so whether your prefer this to Church numerals is a matter of taste. However, for lists/tuples the ability to execute head/tail/cons in constant time is much more important in practice than being able to do append in constant time.
just Scott encoding, Scott-Mogensen refers to a meta encoding of LC in LC. Scott's encoding is fine but requires fixpoint recursion for many operations as you said.
Interestingly though, Mogensen's ternary encoding [1] does not require fixpoint recursion and is the most efficient (wrt being compact) encoding in LC known right now.
> Just use [..], seriously
do you have any further arguments for Scott's encoding? There are many number encodings with constant time predecessor, and with any number requiring O(n) space and `add` being this complex, it becomes quite hard to like
If it helps, you can find a treatment of this in Knuth's vol 4, I believe. Will have to see if I can actually solve it later.
Amusingly, this same basic problem was given to me by Google as an interview question. I was baffled, as I could name the problem, and knew of a reference book that covered it; but I wasn't able to just "on the fly" solve it. Felt like I was living a strawman of pointlessly difficult interview questions. (To be fully fair, I'm not sure I bombed this part. Been far too long for me to remember the other parts.)
Looking at https://text.marvinborner.de/2023-04-06-01.html helped me understand the syntax a bit (though I'm just a non-theoretical programmer).
I was confused about what <4> = \lambda ^ 5 4 meant, since it already seemed to have a "4" in it.
The trick is that the 4 seems to be similar to a positional argument index, but numbered inside out.
IOW, in this encoding, <4> is a function that can be called 5 times (the exponent on the lambda) and upon the fifth call will resolve to whatever was passed in 1st (which because of the inside-out ordering is labeled "4").
(For a simpler example, 0 is a function that can be called once and returns its argument.)
So succ is 3-ary; it says, give me a function (index 2, outermost call); next, give me its first argument (index 1, second-outermost call); when you call that (index 0, dropped, innermost call), I'll apply the function to the argument.
But note that if index 2 is a numeral <N>, the outermost call returns a function that will "remember" the next thing passed in and return it after 1 (succ's innermost call) + N + 1 (<N>'s contract) calls.
> Christopher Wadsworth analyzed different properties of numeral systems and the requirements they have to fulfill to be useful for arithmetic.
> Specifically, he calls a numeral system adequate if it allows for a successor (succ) function, predecessor (pred) function, and a zero? function yielding a true (false) encoding when a number is zero (or not).
A numeral system is adequate iff it can be converted to and from Church numerals. Converting from Church numerals requires functions N0 and Nsucc so that
while converting to Church numerals requires functions Nzero? and Npred so that with an implicit use of the fixpoint combinator.An interesting adequate numeral system is what i call the tuple numerals [1], which are simply iterates of the 1-tuple function T = λxλy.y x
So N0 = id, Nsucc = λnλx.n (T x), Npred = λnλx.n x id, and Nzero? = λnλtλf. n (K t) (K f).
These tuple numerals are useful in proving lower bounds on a functional busy beaver [2].
[1] https://github.com/tromp/AIT/blob/master/numerals/tuple_nume...
[2] https://oeis.org/A333479 (see bms.lam link)
Thanks, added it to bruijn's standard library [0]. Looks like it has some very interesting properties!
[0]: https://bruijn.marvinborner.de/std/Number_Tuple.bruijn.html
Just use Scott-Mogensen encoding, seriously.
Addition requires explicit recursion, however (since numbers aren't folds), so I guess you'll have to either use Y combinator or closure-convert manually: In any case, arithmetic operations can't be made fully constant-time for obvious reasons so whether your prefer this to Church numerals is a matter of taste. However, for lists/tuples the ability to execute head/tail/cons in constant time is much more important in practice than being able to do append in constant time.> Scott-Mogensen encoding
just Scott encoding, Scott-Mogensen refers to a meta encoding of LC in LC. Scott's encoding is fine but requires fixpoint recursion for many operations as you said.
Interestingly though, Mogensen's ternary encoding [1] does not require fixpoint recursion and is the most efficient (wrt being compact) encoding in LC known right now.
> Just use [..], seriously
do you have any further arguments for Scott's encoding? There are many number encodings with constant time predecessor, and with any number requiring O(n) space and `add` being this complex, it becomes quite hard to like
[1]: https://dl.acm.org/doi/10.5555/646802.705958
If you’re “into” de Bruijn numerals or Project Euler then you might be familiar with this little treat:
https://projecteuler.net/problem=941
Otherwise, have a go and don’t spoil it! (I have failed thus far.)
If it helps, you can find a treatment of this in Knuth's vol 4, I believe. Will have to see if I can actually solve it later.
Amusingly, this same basic problem was given to me by Google as an interview question. I was baffled, as I could name the problem, and knew of a reference book that covered it; but I wasn't able to just "on the fly" solve it. Felt like I was living a strawman of pointlessly difficult interview questions. (To be fully fair, I'm not sure I bombed this part. Been far too long for me to remember the other parts.)