From the perspective of my time on Arc Forum, it's great to see Bel. For most purposes Bel is pretty similar to Arc, but the Bel guide goes into pretty extensive detail about how Bel is intended to work, and that's information that was pretty sparse for Arc implementers and maintainers.
The fact that Bel contains a full description of reading, printing, and even arithmetic and thread arbitrarion means when someone tries to implement Bel in a non-Racket language, they'll have a good idea of how much work they're getting themselves into. They won't get lost in Racket documentation trying to understand how Bel works, and they won't find themselves questioning whether they need to implement every single obscure Racket feature just because a Bel program might use it.
I think some of the specific design choices in Bel are a bit wacky, like using mutable lists to represent numbers, functions, and continuations. Frankly I hope people won't invest their time trying to implement it all with perfect precision. Still, the goalposts are far clearer than trying to implement Arc.
---
I'm seeing a few pairs of design choices in Bel that seem to be in opposition, which might be a sign that one of the two is a bug. I suppose I don't know how many of these will turn out to be actual bugs, how many will turn out to be by design, and how many will turn out to be spurious products of my own misunderstanding.
1. Why do missing arguments to prims like `join` and `car` default to nil? In contrast, why do almost none of the functions defined in bel.bel have arguments that default to nil? It seems like the explanation for one of these would apply just as easily to the other, so I'm curious where the difference comes from.
2. Dynamic binding in Bel is a little surprising: "Dynamic bindings take precendence over lexical bindings, which take precedence over global ones." Does that mean we can set up dynamic bindings that interfere with the local lexical bindings of a function we call, like this?
> inc.10
11
> (dyn n 4 inc.10)
5
If Bel's functions expose their local bindings this way, then why do its macros go to the trouble to use gensyms (uvars) to avoid exposing theirs?
3. Exploring another consequence of dynamic binding: Considering the way Bel's macro calls are expanded at run time, can we can interfere with the local variables of macros' macroexpansion logic as well, like this?
If indeed we can do this, then why can't we do the same thing with Bel's special forms? Special forms' implementations are invoked at run time as well, but they're invoked using a plain function call in the interpreter rather than using `applyf`, so it looks like they have access to the dynamic scope of the interpreter host rather than that of the interpreted guest.
4. In the the printer, special care is taken for strings: "strings are lists and can thus share structure with other things we're printing." Shouldn't the printer pay the same kind of attention to numbers, since those are also lists?