Sugar on my tongue

We’ll never do anything as dumb as assert an official programming language here at Paper Tiger but javascript is a language often on my tongue. Alas, javascript has a lot of syntactic sugar.
My problem with syntactic sugar is that I have a very bitter palate. Sometimes it‘s just fondant on top that’s easily removed. Mostly, it’s dissolved in like a too-sweet mocha. Why is it there in the first place? Partly because readability and representational efficiency are important. Even I’ll admit that a[x] += (2 / 3) is generally more comprehensible to a human reader than (list-set! a x (add (list-ref a x) (div 2 3)), especially when you don’t care about the type and order of evaluation of any of it. That’s the upside.
The downside is that sugar is sometimes there to hide crimes. With that, javascript has entered the chat. Javascript is an object-oriented but restricted dialect of Scheme with C-ish syntax. It’s noteworthy for its remarkable performance and for our collective willingness to run javascript code from folks that we distrust on the smart devices closest to us. It’s everywhere, and that‘s what I love about it. The device you’re reading this on probably has an excellent runtime for it, a rich user-interface toolkit, and a credible debugger, even if you have no other programming environment available.
Javascript inherits some problems from its donor languages. One is that interactive is a little dicey. C simply isn’t an interactive language. Build a REPL for C and what syntax form do you even accept? Statement? Statement gives you access to C primitives that are not expressions, like if, while, and declarations, but statements in C have no value — and leave nothing for the REPL to print. It’s probably some messy hybrid of statements and r-values. Beyond that, what is the scope? Are expressions evaluated at global scope? At file scope? Block scope? The answers are not satisfying because they are not obvious. Some languages solve this problems by erasing the distinction between toplevel and not and between expression and not. Some solve it by providing ready tools to invent new interpreters for custom dialects that are suited to interactivity. Javascript simply deflects. Evaluation in Javascript means simply whatever strings that eval() accepts, whatever context it runs in, and whatever it happens to return. It turns out that this is not a simple matter. In the 2024 version of ECMAScript, it’s a 33 step process, of which most of the steps operate only on abstract structures defined in the specification not actually provided by the runtime. One of the wildest details of eval is that eval(“foo = 20”), eval(“var foo = 20”), and (eval)(“var foo = 20”) all mean different things.
You can explore these forever, and maybe should if your goal is program analysis. Mine is mostly doing computering, though, so I want three things: to be able to take a sequence of expressions that I’ve input and paste them into a file, to take a program file and play with it piecemeal in the REPL, and to be able to evaluate simple expressions directly as an interactive calculator. This is surprisingly hard in JavaScript.
Consider the following:
> var foo = 20; undefined > foo; ReferenceError: Can't find variable foo
Oops. Where did foo go? Vars float out of whatever expression or block of expressions they are declared into and are caught by the first enclosing function scope, the global scope if unclaimed. In this case, our REPL evaluator is simply eval(str). This is a so-called direct eval that evaluates in the environment of the enclosing function call — an environment torn down when the function call returns. New declarations are lost. This model doesn’t let me get a lot of computering done.
Switching to an indirect eval, a bizarre javascript gimmick that changes the semantics of eval as if it were a pseudo-keyword. Calling an expression that evaluates to eval is different than directly calling eval. If we switch our REPL evaluator to (eval)(str), we‘re evaluating without an enclosing function scope. Is this better for computering? A bit. eval is crusted with bad sugar.
> var foo = 20; undefined > foo; 20
We‘ve got someplace sensible to keep intermediate values without explicitly attaching them as properties to some global object. Unfortunately, all my stuff gets commingled with everything else at that global scope. I want a durable, private scope. That’s possible! I can combine the relative privacy of a function-scoped direct eval and the durability of the global scope with a javascript trick and a piece of actual computer science. First, the trick. Our evaluator is now a function born at the global scope with the help of the Function constructor — new Function(“str”, “return eval(str);”). In my environment, I declare two variables at the toplevel so I can sanity check. var leak = ”yes, this leaked” and var leakcheck = function() { return leak; }”. Using this hybrid function/direct eval, can I reference the global scope without clobbering it? Yes, but not durable.
var leak = 20; [leak, leakcheck()]; [ 20, "yes, this leaked"] > leak "yes, this leaked"
We created a local binding for leak without clobbering the version used by leakcheck. It was all working well right up to the point where our leak disappeared at the end of the first evaluation. To keep it, I need to have my REPL evaluator return both a value and a new evaluator that captures the ephemeral function scope of the first before it disappears. I need a combinator like:
let _f_comb = "(function(str) { return [ eval(str), eval(_s) ]; });"; let f = (new Function( "_s", "return" + _f_comb))(_f_comb); let repl_eval = function(str) { let r = f(str); f = r[1]; return r[0]; };
Our new evaluator repl_eval returns the result of evaluation and updates its interior evaluator to one that has captured the environment. Now, our leaky example works in a more satisfying way. Although we have a couple of scary strings and evals there, we don’t need to escape the REPL string.
var leak = 20; [leak, leakcheck()]; [ 20, "yes, this leaked"] > leak 20
Nice. Is this a sandbox? Absolutely not. I’m not trying to keep the browser safe from me. I want to interact intimately with the browser and the javascript environment but I want my own nightstand.
Isn’t this the problem that javascript modules were trying to solve? It‘s not totally unrelated. How about web workers? Those solve a completely different problem that has isolation as a side effect. Is there a better way? Very probably. I bet calling eval as doubly indirect with a backslash or something does exactly what I want, or that some new language feature soon will.
Is there a better and sinpler free computer than one I’m already using? I don’t know, but you can load a working REPL from a QR code. That’s why I keep coming back to javascript. Besides, it’s where I keep my toothbrush.
BTW: Here’s javascript in a data URL. Try visiting this URL in your browser for a truly tiny serverless REPL
data:text/html,<html><body onload="while(1) alert(eval(prompt('> ')))">hi</body></html>
Transform this into a QR code, and you get something like:

On iOS, you should be able to copy this to the clipboard this directly from Safari, or by taking a picture or screen capture of this. Paste that into the location for Safari and you should get a little repl that prompts for expressions and alerts with results.
This post pairs well with ”Sugar on my Tongue”, Talking Heads, 1975.