Live data from Hacker News

Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

woodrush.github.io

71–80 of 101 posts

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#71
post #48
post #46

Earlier quoted context omitted.

> it's difficult to write a LC interpreter in LC. Why? It's pretty easy to write Lisp in LC (a simple Lisp, not the feature-full version described in TFA) and it's pretty easy to write an LC interpreter in Lisp. I'll bet that an LC interpreter in LC could be done in only a few hundred lambda terms.

> an LC interpreter in LC could be done in only a few hundred lambda terms. Make that a few dozen...

Yeah, I should have realized that. In fact, I had seen your BLC interpreter some time ago but for some reason it didn't come to mind as I was writing that. I must be getting old.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#72

This is undoubtedly cool, but I'd be really impressed if it wasn't stupidly slow. (I'm not saying it is stupidly slow, because I haven't had a chance to run it, just that I'd be impressed if it wasn't.)

Thanks for your feedback, I forgot to mention the execution speed in the post. The number guessing game example (44 lines long) in the Usage section actually runs with an instantaneous response time on the terminal. I added some remarks on the execution speed.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#73
post #70

Earlier quoted context omitted.

> which is why it took 20 years of further research to go from Lisp 1 to Scheme. Actually it only took Peter Landin a few years. For example see: P. J. Landin, The Mechanical Evaluation of Expressions, The Computer Journal, Volume 6, Issue 4, January 1964, Pages 308–320, https://doi.org/10.1093/comjnl/6.4.308

Thanks for that link! TIL.

Also: MIT's PAL (1968 -- Art Evans, Martin Richards, and others) and John Reynolds' GEDANKEN (1969):

https://www.softwarepreservation.org/projects/PAL/

https://www.softwarepreservation.org/projects/GEDANKEN/

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#74

Earlier quoted context omitted.

I don't see what in the interpreter converts the lambda calculus into the Morgensen-Scott encoding. The Wikipedia page describes a "mse" function that is in some meta-language which is not lambda calculus. So first wee need a Lambda Calculus based interpreter for the meta-language, which can run this "mse" function. It looks like mse[x] is supposed to match a variable term, and mse[M N] matches a function application…

The situation with Lisp is exactly the same. To run a Lisp self-interpreter, we don’t pass it a Lisp function: (interpret (lambda (x) x)) but rather an encoded version of that Lisp function’s code: (interpret (cons 'lambda (cons (cons 'x nil) (cons 'x nil))) Of course, Lisp gives us a more convenient syntax for the latter, in the form of the quote macro: (interpret (quote (lambda (x) x))) (interpret '(lambda (x) x))…

> The situation with Lisp is exactly the same.

No it isn't, because the Lisp code is already understood to have an encoding. So we don't have to play any Gödel-numbering-like games to get the code to be able to talk about code. That battery is included.

> gives us a more convenient syntax for the latter, in the form of the quote macro

The ' in (cons 'lambda ...) is an instance of quote!

You must write (cons (intern "lambda") ...) to remove quote. Oops, now you're using a different kind of quote: a string literal quote. If you remove that, you will have character literals to otherwise build the symbol name.

I agree that quote is not essential: take out quote and you can still do useful symbolic processing. Just doing interactive testing and writing unit tests will be inconvenient, mainly.

The requirement for quote has a different effect. If we have quote, we can make the additional step in the documentation that all code has the representation produced by quote, even when quote is not being used. When lambda is seen in code, that is actually the same thing that (quote lambda) produces or that (intern "lambda") produces.

The above is almost inescapable if user-defined macros are supported. When code is read, it is not determined at read time what is a macro and what isn't. Therefore it is not known what parts of the form may need to be passed to a user-defined expander function without having been evaluated (and thus in the quote representation). The whole thing is in the quoted encoding, so that quote doesn't have to do anything other than pass through its interior.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#75

Earlier quoted context omitted.

> BLC requires translating bitstrings into lambda terms, to which the machine (itself a lambda term) can be readily applied. I.e. a man behind the curtain is required to complete the "interpreter".

Can you explain at a high level what you think a valid LC-in-LC interpreter looks like then? Because it's very hard to understand what your objection is to the examples in this thread.

The objection to them is that they are trivial and lead to none of the profound truth that is usually implied by their presentation.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#76
post #62

Earlier quoted context omitted.

> would take a Mogensen encoding obtained where? > output a Mogensen encoding of that Mogensen encoding That's not what a quote operator does; it does precisely nothing, yielding the argument formula without evaluating it. No encoding-of-encoding. Just the encoding. > output the blc encoding of the nil-terminated list of 4 booleans that represents that bitstring Where/how does that become λ 1 again?

> That's not what a quote operator does; it does precisely nothing, This is what gives Lisp murky semantics; you need something (quote) to do nothing, while having nothing (no quote) does something (evaluate). Lisp lacks referential transparency, even without the use of variables. An evaluated term can be evaluated again, yielding something different. > Where/how does that become λ 1 again? By decoding it, which is w…

> An evaluated term can be evaluated again, yielding something different.

Yes, and a Mogensen-Scott encoding can be Mogensen-Scott-encoded again, requiring two rounds of decoding, and so on.

Multiple rounds of encoding and evaluation seem inescapable of you have the entanglement of homoiconicity.

The quote operator in Lisp is designed exactly right.

In mathematics there are literals like the number 3 or the set {}. These objects stand for themselves and are not understood as requiring any calculation: they just are.

Symbols like x do not stand for themselves. If you want to talk about x literally as the symbol object, it is inescapable that there is some quoting operator to indicate that the usual semantics of x denoting something else do not apply. (Oxford's A Dictionary of Computer Science has a definition of literal which acknowledges this very issue.)

Literals being constants, it means that when they are concretely implemented in a computer, in the best possible way, they do nothing other than trivially reproduce a canned value that already exists before the program starts.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#77
post #44

Earlier quoted context omitted.

The complexity topic from which we have P = NP is not actually about time, but number of steps. Of course that matters physically because if you only have a machine that performs one step at a time (or a somewhat better one that performs N steps at a time, for some fixed N), then the number of steps does translate to amount of time. If you have access to unlimited parallelism, then, for instance, some recursive algor…

> Of course that matters physically because if you only have a machine that performs one step at a time (or a somewhat better one that performs N steps at a time, for some fixed N), then the number of steps does translate to amount of time. It's much more fundamental than that. If you have any finite machine then the amount of time it takes to perform N steps will be proportional to N for sufficiently large N. > If y…

For sufficiently large N, all computation that isn't O(1) takes infinite time.

Complexity analysis goes out to infinity, but the way we use it as a tool is to inform us about what happens with our practical, finite inputs.

We know that something requires a non-polynomial number of steps, it quickly becomes untractable for small inputs. (So if we code that in a practical program, we need some justification that either the inputs won't occur, or we actively guard against them.)

Whether the entire universe is actually finite is not known, and not really relevant because that subset of it which is available to us as resources is vastly tiny.

The universe is vast, and vastly parallel: things are happening all over it at once, with more objects than have ever been crammed into any of our computing machines.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#78

Earlier quoted context omitted.

> BLC requires translating bitstrings into lambda terms, to which the machine (itself a lambda term) can be readily applied. I.e. a man behind the curtain is required to complete the "interpreter".

Can you explain at a high level what you think a valid LC-in-LC interpreter looks like then? Because it's very hard to understand what your objection is to the examples in this thread.

A LC interpreter (whether in LC or anything else) has to process the actual syntax with the lambdas and symbols (or integer literals, in the case of De Bruin).

If it is necessary to translate that representation to something else, the interpreter must do that itself.

You can take a LC expression and write some other LC expression which encodes it; but if that is done outside of LC in some meta-language, then that has to be counted as part of the interpreter's implementation, which means that it's not in LC any more.

Based on that, I think it's not actually possible without extensions to LC.

You need to be able to read the syntax with the lambda symbols and names (or integer constants in the case of De Bruin), so I/O is needed. Or else, if that is unacceptable, you need to be able to embed the representation of the code as a quoted literal.

Without I/O or quoting, you have no way to express the test cases.

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#79

Earlier quoted context omitted.

Can you explain at a high level what you think a valid LC-in-LC interpreter looks like then? Because it's very hard to understand what your objection is to the examples in this thread.

A LC interpreter (whether in LC or anything else) has to process the actual syntax with the lambdas and symbols (or integer literals, in the case of De Bruin). If it is necessary to translate that representation to something else, the interpreter must do that itself. You can take a LC expression and write some other LC expression which encodes it; but if that is done outside of LC in some meta-language, then that has…

The basic tools required for implementing a LC interpreter in LC is demonstrated in LambdaLisp. Although LambdaLisp implements Lisp, the same tools can be used to implement a LC interpreter in LC.

The largest concern in question here may be how I/O is done. I/O can be done by viewing a lambda term as a function that takes a string as an input and outputs a string. Here, a "string" can be expressed as a "list" of "characters", where lists can be encoded using the Scott encoding, and characters can be encoded as a list of bits, and "bits" can be encoded as 0=\x.\y.x, 1=\x.\y.y. This I/O strategy is used in LambdaLisp as well, and is explained in my post in [1].

How Lisp terms are encoded as lambda terms in LambaLisp is explained in [2].

To implement a LC interpreter instead of a Lisp interpreter in LC, it then remains to somehow encode and quote a lambda calculus term into a lambda calculus term. The function described by tromp's reply in this thread, called the Universal Machine, does this - it does so by using an encoding called the binary lambda calculus (BLC) notation for encoding lambda terms into lambda terms. In this notation, an arbitrary lambda term in the usual plaintext notation such as \x.\y.\z.(x y z) can be encoded into a bitstream. The converse is also always possible. The Universal Machine uses the BLC notation for expressing lambda terms, with the same 0/1 bit encoding and list encoding mentioned earlier, and outputs a bitstream (encoded as lambda terms) representing the evaluation result. The BLC notation is described in my post in [3].

[1] https://woodrush.github.io/blog/lambdalisp.html#handling-io-...

[2] https://woodrush.github.io/blog/lambdalisp.html#basic-data-s...

[3] https://woodrush.github.io/blog/lambdalisp.html#the-binary-l...

Re: Show HN: LambdaLisp – A Lisp interpreter that runs on lambda calculus

#80

Earlier quoted context omitted.

The situation with Lisp is exactly the same. To run a Lisp self-interpreter, we don’t pass it a Lisp function: (interpret (lambda (x) x)) but rather an encoded version of that Lisp function’s code: (interpret (cons 'lambda (cons (cons 'x nil) (cons 'x nil))) Of course, Lisp gives us a more convenient syntax for the latter, in the form of the quote macro: (interpret (quote (lambda (x) x))) (interpret '(lambda (x) x))…

> The situation with Lisp is exactly the same. No it isn't, because the Lisp code is already understood to have an encoding. So we don't have to play any Gödel-numbering-like games to get the code to be able to talk about code. That battery is included. > gives us a more convenient syntax for the latter, in the form of the quote macro The ' in (cons 'lambda ...) is an instance of quote! You must write (cons (intern "…

Yes, I was clear above that I know ' means quote. My experiment is to compare Lisp to a restriction of Lisp where quote only works on symbols. This restriction doesn’t make it any harder to write a self-interpreter.

> If we have quote, we can make the additional step in the documentation that all code has the representation produced by quote, even when quote is not being used. When lambda is seen in code, that is actually the same thing that (quote lambda) produces or that (intern "lambda") produces.

I’m not sure what you’re suggesting here. Certainly the number (+ 2 2) must be distinguishable from the list '(+ 2 2). Even lambda expressions must be distinguishable from their quoted encodings, because otherwise lexical scoping breaks. If you make (lambda () x) equivalent to '(lambda () x), then this breaks:

    (let ((x 1)) (funcall (let ((x 2)) (lambda () x))))
and if you make (lambda () x) equivalent to `(lambda () ',x), then this breaks:

    (let ((x 1)) (funcall (lambda () (let ((x 2)) x))))
Macros expand at compile time, not runtime. Macros are also cool, but not fundamental, and don’t contribute to the ease of writing a self-interpreter.
Post reply on HN