> I don't think you understand what you're criticizing.
Oh, I'm pretty sure I do.
Functions are represented using lists. For example, here is a function that takes one argument, and returns that plus 1.
(lit clo nil (x) (+ x 1))
The first element, lit, says that this is a literal object, not to be evaluated.
What you want in this kind of situation is for those symbols to be internal objects that cannot be written. Or perhaps symbols in a private system package, e.g.:
(sys:lit sys:clo nil (x) (+ x 1))
No program using symbols in the ordinary namespace will produce this object by accident. It has to use the sys: stuff and when it does that, the language specification can say that it's sticking the proverbial fork into the proverbial toaster.
That doesn't help at all. These symbols don't have to be produced by the reader. All you need to do to get access to these symbols is to call FN once. That gives you a list whose first element is LIT and second element is CLO. So all you need to "stick a fork" in the Bel toaster is to be able to call FN and XAR.
Yes. In a nutshell, the problem is that (consp x) being true does not rule out (functionp x), which it should. (Unless consp is hacked in an unspeakable way.)
In a real implementation, you'd want a new kind of cons cell with a separate type tag so that it fails consp. It's okay if car and cdr like it.
> (consp x) being true does not rule out (functionp x), which it should
Exactly. That's what "punning lists" means.
> It's okay if car and cdr like it.
That's arguable. But the real problem is if XAR and XDR accept it. Functions, however they are implemented, should not be mutable. That way lies madness.
The disconnect is that PG misuses the word "represented". Functions in Bel are not just represented as lists, they are lists. Specifically, they are lists whose first element is the symbol LIT and whose second element is the symbol CLO. Functions can also be represented as lists that start with the symbol FN.
You would not ordinarily express a function using its literal representation. Usually you'd say
(fn (x) (+ x 1))
which yields the function above. [i.e. (lit clo nil (x) (+ x 1))]
So a list that starts with FN represents a function, but it is not a function. It has to be evaluated to produce a function, but the function that it produces is just another list, one which starts with LIT rather than FN.
In Bel you have to eval (fn (x) (+ x 1)) before you can call it. Do you agree? Following your terms in https://news.ycombinator.com/item?id=40409467, in this respect Bel feels like a modern lisp. In other words, this quoted passage of yours is not correct:
(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
Yes, that's right, but you have missed the point. In McCarthy's Lisp, functions were lists whose first element was the symbol LAMBDA. In Bel, functions are lists whose first element is LIT and whose second element is CLO. But the details of what is in those lists is irrelevant. The point is, in both langauges, functions are lists. You can call CAR on a function and get a result. In McCarthy's Lisp, that result will be LAMBDA and in Bel it will be LIT. If you call CAR on a function in a modern Lisp you will get an error.
? (car (lambda () t))
> Error: The value #<Anonymous Function #x302000E7B61F> is not of the expected type LIST.
Contrast with:
? (car '(lambda () t))
LAMBDA
In addition, there is a special form in BEL called FN which, when evaluated, produces a function, i.e. a list whose first element is LIT and whose second element is CLO. McCarthy's original lisp had no such function. The special handling of functions, i.e. lists starting with LAMBDA, was hard-coded into the definition of EVAL. But that is also an irrelevant detail.
I don't understand why this makes it hard to compile Bel. Application code won't check the first two elements of the list to determine if a term is a function. Instead it will say ((isa 'function) ___). It seems fine that a program using the former approach will run slower.
> I don't understand why this makes it hard to compile Bel.
Because Bel is supposed to be a specification, not an implementation. And if you specify that functions are lists then you are no longer free to implement them in a way that does not preserve the illusion that they are lists. Worse, in Bel, lists are mutable. That makes reasoning about programs nearly impossible. Every time you call CONS (which in Bel is spelled JOIN for some reason) you may or may not be producing a function. Every time you mutate a pair you may or may not be creating a function, or destroying one, or changing a function's environment.
There is not and cannot be any barrier between the mutable and the immutable in Bel. It is not possible to add a data type that guarantees invariants. Mutable pairs are all you have to build with, and so whatever you build, not matter how carefully you design it, the rug can get pulled out from under you at any time by an errant call to XAR or XDR.
Oh, I'm pretty sure I do.