Encapsulation by Obfuscation
A while ago we explained
what the >> syntax stands for:
it auto-names an abstract object when a meaningful name is unnecessary,
and the compiler quietly generates a random unique one under the hood.
Recently we let this same >> carry a handle of your choosing,
and this small change gave EO a new way to hide things.
We call it “encapsulation by obfuscation”.
The Problem
Most languages guard a name with a keyword.
Java has private and Swift has fileprivate;
Python merely asks you nicely with a leading underscore.
In each case the attribute keeps its original name,
and enforcement ranges from a compiler error to a matter of etiquette.
The name is still there,
still spelled the way the author spelled it,
and a sufficiently motivated caller (reflection, a mangling trick, a cast)
can almost always reach it.
EO has no visibility keywords, and we did not want to add any.
Yet a .eo file often needs a helper object
that makes sense only inside that file
and should never be addressed from anywhere else.
How do you make something private in a language that has no word for “private”?
The Idea
You cannot refer to a name you do not know. That is the whole trick.
Consider an object that clamps a number to at most ten, leaning on a small helper to take the absolute value first:
[n] > out
if. > @
(abs n).gt 10
10
abs n
[x] > abs
if. > @
x.gt 0
x
x.neg
The abs object is a true helper.
It exists only to serve out, and it means nothing on its own.
Yet, declared with a plain > abs,
it becomes a public attribute of out,
and any other file can reach it:
(out 42).abs
That call is nonsense, but the compiler happily allows it,
because abs is a visible name.
We would rather it were impossible.
So we change one character:
[x] >> abs
Now abs is a file-local handle.
Inside the file, out still calls it exactly as before.
Outside the file, abs simply does not exist,
and (out 42).abs no longer compiles.
Under the Hood
The compiler treats abs as nothing more than a temporary label.
During parsing it assigns the object a deterministic “cactus” name
derived from the line and column where it was declared,
collects a per-file table of handles,
and rewrites every mention of abs to that cactus name.
The name it picks is a🌵6-3 —
an a, the cactus emoji, and the coordinates of the declaration.
What comes out the other end looks roughly like this:
[n] > out
if. > @
(a🌵6-3 n).gt 10
10
a🌵6-3 n
[x] > a🌵6-3
if. > @
x.gt 0
x
x.neg
The helper is still there, still perfectly usable —
out clamps its argument exactly as before,
because both call sites were rewritten to the same cactus name.
But its name now contains 🌵,
and the cactus emoji is deliberately excluded
from the grammar of a valid identifier.
No programmer can type it, in this file or any other.
Another file that tries (out 42).abs finds no abs;
and it cannot ask for a🌵6-3 either,
because it neither knows the coordinates nor is allowed to spell the cactus.
The attribute is not hidden behind a rule that says “do not look”.
It is hidden because there is nothing left to look for.
Why “Obfuscation”
We are not pretending this idea is new;
it is inherited from decades of language design.
Lisp’s gensym has minted unwriteable symbols since the 1970s,
hygienic macros rename identifiers precisely
so surrounding code cannot capture them,
Python’s double underscore mangles a name into a different one,
and every JavaScript minifier renames locals
so the outside world cannot reach them.
The object-capability model makes the same wager at runtime:
authority you were never handed is authority you cannot name.
What EO does is take that old idea — privacy through an unspeakable name —
and make it the language’s only encapsulation mechanism,
operating at file scope, with no keyword and no annotation.
The compiler obfuscates the name; the obfuscation is the encapsulation.
Hence the honest label.
The pleasant part is that it costs the programmer nothing.
You write a friendly abs and read a friendly abs everywhere in your file.
The obfuscation happens once, at compile time,
and only the compiler ever sees the ugly name.
You get the readability of a normal identifier and the privacy of a secret one,
without a single access modifier.
That’s all for today.
Give >> name a try the next time a helper object should stay at home.