Syntactic umami
Yeah, more posts about building (software) and food.
Our last post was about how small changes in javascript syntax lead to surprising changes in meaning. Syntactic sugar usually refers to convenience features in the syntax of a language that make code more readable at the expense of some complexity in the parser, and further complexity for any program analysis tools that need to correctly normalize idiosyncratic forms. Think a.x vs a[“x”] or ref(a, “x”) or some more oppressively normalized form.
I don’t know that there is a really good definition for syntactic umami. It’s complex and savory syntax that seems like maybe it’s just a simpler version of something but which defies attempts to normalize or reimplement. Maybe it’s somewhat elusive syntax that‘s long deprecated in the language but still widely supported.
A terrific example in javascript is ‘with’.
In many programming languages, word-like tokens like “int”, “main”, “for”, “while”, “foo_bar_123” represent types or variables or language keywords. Any given language may have multiple different namespaces that tolerate collisions cheerfully and resolve them in an obvious way. In C, the type namespace, the variable namespace, and the namespace of structure fields are separate.
struct foo { int foo; int bar; }; typedef struct foo bar; int main(int argc, char **argv) { struct foo foo; bar *bar = &foo; return foo.foo + bar->bar; }
In javascript, things are a little murkier. We have none of those pesky types to worry about but the references that look like variables are not all variables. Here’s a complete javascript program:
foo = 1
May not look exciting, but quiet bindings run deep. Is foo a variable? We have no idea — it depends on what else has been processed by runtime prior to loading this program. Could it be a macro? No, javascript lacks this essential Scheme feature. If it’s not a variable, or a macro, or a keyword, then what else could it be?
foo = 1; alert(window.foo);
In this case, foo had never previously been declared as a variable, so it was understood to be an implied property set on a global object. In the browser, that global object is ‘window’. So foo = 1 is equvalent to window.foo = 1 is equivalent to window[“foo”] = 1.
So if the variable declarations are the circulatory system of javascript, this whole implied property accessor thing is the lymphatic system that nobody really understands except for doctors and herbalists. Also, when your global object gets swollen, your runtime is sick. But just as the the, uhh, spleen connects the lymphatic system to the circulatory system, so too are the implied accessors and variables connected.
Whenever you declare a variable with ‘var foo’, a variable is created in the closest dominating function or global scope. If it’s in the global scope, a property with the same name is created in the global object. When you simply force the property into existence by referencing it or directly manipulating the global object, no such variable is created. In most programs, this is a distinction without a difference. Here’s a program where it does matter
with({"foo": 33}) var foo=-33; alert(foo);
So what’s the displayed value of foo? 33 or -33? Neither, it’s undefined. Declared, but undefined. Here’s why.
“with” is a syntax construction that adds a new object at the head in front of whatever the current global object is. Implied accesses search that object first, then the global object. ”with” statements can be stacked, so this search can proceed through arbitrarily many levels until the global is reached. But the detail about global variable declarations creating a property on the global object does not move through this chain — even if the named variable is somewhere in it. That means a new property is created on the global object, but not used for accesses or updates satisfied in the chain.
Is this a bug? No. It’s the defined behavior of the language. Is this the real reason the ”with” syntax is deprecated? I don’t really know. The stated reasons are that it’s too confusing (which is to say that most javascript programmers got in about elbow deep before realizing the language was not either the java or the script they had expected) and difficult to optimize. Why didn’t they change the semantics to search the chain for an existing property before creating a new one at the top level? That one I do understand. One of the things that keeps the oldest sites on the web humming is careful attention to not breaking old programs. You may often be told that your browser is too old, but rarely that your browser is too new.
Here’s a trivial piece of javascript on a page from 1998 — from the oldest version captured by archive.org in 2009:

Still works. Not only does it work, but it survived the archive.org process.
”with” exposes a structured resolution system in the language that may even be the same mechanism used for lexically scoped variable resolution but neither can “with” easily replace those mechanisms nor can they recreate “with”.
with({x: 20}) { statements ... }
… is almost the same as
(function(x) { statements ... })(20)
Except that “with“ is a statement and the function invocation is an expression. And that return statements in the have a different meaning. And that new variables declared in the function do not leak into global object. So hardly the same at all.
Maybe it’s almost the same as
{ let x = 20; statements ... }
Except that a block with a let may not also define the same variable with “var”.
Is “with” bad? Should it be eliminated? Hardly. Savory, ineffable, and difficult to reproduce? Yes — that’s syntactic umami.