"If the interpreter never throws errors it lets you build other languages atop it without abstraction leakage."
I don't see the advantage. If calling 'car on a non-cons gives me a value that I can't tell was caused by an error, then if I want a leak-proof abstraction I'll do anything I can to detect the error in a separate step from calling 'car, just as I would if 'car raised an exception.
An exception with an error message is at least nice for detecting that there's a problem and troubleshooting it, even if the message is as obscure as "Can't take car of 1".
---
"TCO is just an optimization and has nothing to do with the evaluation model ^_^."
Tell that to my lathe.js and chops.js, which I'm having trouble getting to work on Firefox because its JavaScript stack is small. XD I have a lot of ifsomething( ... ) utilities that take callbacks and tail-call them (CPS), and that pattern just doesn't seem to work out. :-p
Anyway, I'm not complaining about JavaScript necessarily. I knew about the lack of TCO from the outset. Whatever way this forces me to redesign lathe.js will help with Penknife, which I don't expect to have TCO either (for straightforwardness's sake, and because unfortunately Penknife/Lathe/'extend rulebooks don't call their rules in tail position anyway).
---
"I don't even know what sort of support I want for early returns."
That can be implemented in a standard library as long as there are exceptions that can hold arbitrary contents. I implement early returns in lathe.js's point(), for instance--which, since it isn't a standard part of JS, means it doesn't necessarily interact well with other people's exception-handling code. :-p
---
"I don't understand continuations well enough to implement them."
What you're doing with pushing and popping lexical and dynamic scopes seems about halfway to continuations. If you also push expressions on a stack and evaluate them in a separate loop, the combination of scope stacks and an expression stack is basically a continuation. To capture a continuation, copy the stack(s); to call it, restore the stack(s) and push a return value. Something like that anyway. :-p (If you don't understand continuations from a language user perspective, I guess I don't expect this explanation to help.)
I haven't tried to implement partial evaluation/specialization yet, but I expect it to kinda work the same way. To specialize a function, start some fresh stack(s) for it with special dummy values in place of the arguments, somehow step the calculation forward as far as possible (without doing I/O--including variable lookup--or doing any inspection of the dummy values), and copy the stack(s) back off as the result. I think it'd be pretty difficult. ><;
---
"What would unifying strings and syms accomplish?"
For me it's more like: What does separating them accomplish? The way I use Arc, they're both interned, immutable, finite sequences of arbitrary characters. The fact that they have different types means the meaning of a single character sequence can be overloaded ("nil", especially), but for the most part I just coerce them back and forth to appease the language.
"If calling 'car on a non-cons gives me a value that I can't tell was caused by an error, then if I want a leak-proof abstraction I'll do anything I can to detect the error in a separate step from calling 'car, just as I would if 'car raised an exception."
Yeah you're right, I was making no sense. Or at least I've forgotten the concrete situation I was (over) reacting to.
Wart does throw a message: 'car of non-cons: 1'. It's just a warning, it doesn't halt execution, but I'm still contradicting myself by that decision.
I'll note that in my code I frequently have to use errsafe, because the thing may not be a cons. For instance, suppose you had a table called "foo". I've been using this pattern a lot recently:
(if (foo:car x) ...)
But that will break if x isn't a cons, so I have to do this:
(if (errsafe:foo:car x) ...)
So I don't think it's necessarily a bad idea for car/cdr to return a value rather than throw an error... but I can see why some people would prefer it to throw an error. For me personally, I think my code would benefit more from car/cdr returning nil, rather than throwing an error.