8 ms·
Well, it's a noble goal, but fails rather spectacularly right off the bat. > Bel has four fundamental data types: symbols, pairs, characters, and streams. The
by lisper 2y ago
Well, it's a noble goal, but fails rather spectacularly right off the bat.
> Bel has four fundamental data types: symbols, pairs, characters, and streams.
The inclusion of streams is already enough to make me go WTF, and the absence of numbers, vectors, and functions as primitives is a big yellow flag. Punning lists as functions is huge blunder because it makes it nearly impossible to compile the language.
- smj-edison 2y ago> I want to be clear about what Bel is and isn't. Although it has a lot more features than McCarthy's 1960 Lisp, it's still only the product of the formal phase. This is not a language you can use to program computers, just as the Lisp in the 1960 paper wasn't. Mainly because, like McCarthy's Lisp, it is not at all concerned with efficiency. When I define append in Bel, I'm saying what append means, not trying to provide an efficient implementation of it. That doesn't seem to be their goal.
- lisper 2y agoOK, that's a fair point. But punning lists as functions is a still mistake for other reasons. For example, it makes it impossible to tell the difference between a list that is actually intended to be a function, and one that just happens to end up looking like a function by happenstance. It's fine to represent functions as lists, but having functions actually be lists rather than a first-class primitive is a huge mistake. If you're going to do that, you might as well just start with McCarthy's original primitives, or heck, even just the untyped lambda calculus. What is the point of adding characters and streams?
- sctb 2y agoPerhaps to make it a real programming language that you could use to do stuff?
- akkartik 2y agoI think one reason to add characters and streams is so you can implement read and print, the R and the P in a Lisp REPL. The original Lisp assumes s-expressions are given, but provides no mechanism for creating them out of characters read from keyboard or printed to screen. Bel is taking the same axiomatic approach of the original Lisp but to a more realistic system. Similarly, ccc exists so onerr can exist, which exists so saferead can exist, which exists so the REPL can show errors on screen. That hangs together for me. But I still don't understand the distinction you're making between representing as lists vs being lists.
- lisper 2y ago> so the REPL can show errors on screen And how do you formalize the screen? (BTW, this is just speculation on your part. The word "screen" doesn't actually appear anywhere in the article.) You can't have it both ways. Either you are going to formalize everything, or you're going to punt at some point and yield to practical considerations. And formalizing I/O is particularly hard. > I still don't understand the distinction you're making between representing as lists vs being lists. (lambda () t) is a list. In McCarthy's original lisp, as in Bel, this list IS a function. This would work: (setq l '(lambda () t)) (l) --> T But in modern Lisps you have to EVALUATE this list in order to get a function that you can call. In Scheme: ts> (define l '(lambda () #t)) l ts> l (lambda () #t) ts> (l) Error: illegal function ts> (define p (eval l)) p ts> p #<CLOSURE> ts> (p) #t ts> (procedure? l) #f ts> (procedure? p) #t ts> (list? l) #t ts> (list? p) #f
- akkartik 2y ago> (lambda () t) is a list. This is not right, and it's a very basic thing to get wrong. I don't think you understand what you're criticizing. Even replacing the Bel name, this is not right: > (fn () t) is a list You _do_ have to eval it. bel> (eval '(fn () t)) (lit clo nil () t) I see the returned list as akin to a serialization format. It's an s-expression rather than some opaque representation that can't be printed. That seems nice, more debuggable. I don't see what makes it harder to compile than an opaque representation. You can compile it just fine, you're just carrying around the source code in say a debug segment of the image. > (BTW, this is just speculation on your part. The word "screen" doesn't actually appear anywhere in the article.) Look, you asked what the point of streams was. I pointed out what the metacircular source code does with streams. If someone asks what the purpose of stdout is in Unix, it seems reasonable (and a hoary tradition) to justify it in terms of printing to screen. stdout is about much more than a screen, but "screen" is an easy answer to "why does my program need stdout?"
- smj-edison 2y agoTo be honest, as I've read more of the Bel document, I have to say streams and characters feel rather "bolted on", especially since streams only work with binary. It feels like it's trying to straddle both the practical and theoretical, and ends up being a bit of a mess. I appreciate people exploring new concepts, but honestly this doesn't feel that novel.
- James_K 2y ago> But punning lists as functions is a still mistake for other reasons Formally speaking, you would struggle to do it any other way. If you introduce a type for functions, you need some way to get to it from lists in order for macros to work. Then that totally muddies the definition of eval. You'd basically have to replace it with some "compile" function that turns an expression into a set of lambdas then executes them. One starts to question the need for lists at all at that point, but when you start representing data as lambdas you lose the well-foundedness of lists. Suddenly lambda isn't something you can write down but just an output from "compile". If you wanted to write lambda in terms of lists, they would need be written in terms of lambdas and you end up with an infinitely recursive problem. So lists are never really written down, just constructed by "read" and input to "compile". What you have at that point is no longer lisp, but just a compiler to lambda calculus. I think the lesson here is really that lisp is a convenient syntax but a lousy model for computation. From that regard, saying "I'll do a purely formal lisp" is a mistake. The formal component of lisp is a waste of time and only its practical aspects are useful.
- lisper 2y ago> Formally speaking, you would struggle to do it any other way. Um... why? > If you introduce a type for functions, you need some way to get to it from lists in order for macros to work. Sure. So? That has been a solved problem for over fifty years. > Then that totally muddies the definition of eval. No, it doesn't. There is absolutely no reason that a function has to be represented as a list. The only reason it was represented as a list in McCarthy's original design is because there was nothing else. See here: https://flownet.com/ron/l.py https://flownet.com/ron/l.py for an example of a Lisp interpreter written in Python where functions are represented as Python objects. You will note that the other than being written in Python, the definition of eval is not at all muddled. > I think the lesson here is really that lisp is a convenient syntax but a lousy model for computation. I think you don't know what you're talking about. The syntax has absolutely nothing to do with punning lists. You can pun lists in any language, and it's an equally bad idea in any language.
- James_K 2y ago
- James_K 2y ago> it makes it nearly impossible to compile the language You would honestly be surprised what little difference it makes. You can just go through the source code as you compile it and compile instances of function-like objects with the compiled version. If you imagine a data-type for lists: class List { Object car; Object cdr; }; class Compiled_List inherits List { Object (*call)(List args); } Then you would have function calls compile to: // compiled value of (F . ARGS) if (F is Compiled_List) return F->call(ARGS); else if (F is List) // interpret F This is no slower than the compiled version of a regular function call in any other lisp, since there is also a need to type-check the function there. The real issue is the lack of numbers if you are interested in speed.
- lisper 2y agoThat doesn't work because there are only four primitive types: symbols, pairs, characters, and streams. You can't add more. Bel is supposed to be a specification, not an implementation. So pairs are the only compound data type. That is all you have to build more complex stuff out of. Even lists are puns on pairs. So there is no place to hide any adjunct data without violating the spec. This is one of the many reasons that an operational spec is a bad idea. I am as big a fan of Lisp as you can hope to find (just look at my user name) but PG's infatuation with McCarthy's original definition of EVAL is badly misguided. McCarthy's EVAL was NOT a formal spec of anything, it was an illustration that CONS, CAR, CDR, QUOTE and COND were a really good choice of computational primitives because you could write EVAL in a few dozen lines rather than the pages and pages required to do the same thing for a Turing machine.
- James_K 2y ago> That is all you have to build more complex stuff out of By this logic you can't add type information to anything because you can only use pairs so there is nowhere to store the type. It's a silly thing to say because it assumes the compiler for the language has to be written in the language. > So there is no place to hide any adjunct data without violating the spec The spec is that you have cons, car, and cdr. You could have a secret third element to some pairs that is only used by the compiler without changing the interface of those functions, and hence without changing the interface of the language as a whole. This is the essence of optimising compilers, making changes to the representation of things without changing how they behave. > PG's infatuation with McCarthy's original definition of EVAL is badly misguided I agree wholeheartedly with this. This entire document is basically just an extended brain-fart but because it came out of a rich guy's brain-ass a bunch of people will treat it like it's serious.