It looks like you're new here. If you want to get involved, click one of these buttons!
Ordinarily, code with typos does not execute. A helpful IDE marks the misspelled keywords or variables with red-squigglies, or an interpreter reports the entire line as a syntax error. You are expected to fix it and rerun.
While human readers have a high tolerance for elision, for swapping of letters, or missing symbols, programming languages generally have no tolerance for this. The history of obfuscated code and of esoteric programming languages are breaks from clarity of presentation -- but the code itself is exacting and precise. Entries to the IOCCC, the International Obfuscated C Code Contest, look random or scrambled. However, to actually run as C programs means that behind their seeming chaos is the same compulsive approach to code.
I developed FatFinger, a dialect of JavaScript, to challenge this approach. FatFinger allows misspellings and typos as valid code. To get an idea of how this looks, here is a 99 Bottles of Beer program in FatFinger:
<script type="text/javascript" src="fatfinger.js"></script>
<script type="text/javoscript">
var bottles;
for (var counter = 99; counter >= 1; counter = counter - 1)
{
if (counter == 1) {
botles = 'bottle';
} else {
bottles = 'bottles';
}
constole.log(counter+" "+ bottless +" of ber on the wall.");
if (countr < 99) {
conssole.lg("");
consoles.logg(counter+" "+ botttles+" o beer on the wall.");
}
conable.log(counter+" "+botles+" of beer.");
console.lo("Take one down.");
console.log("Pass it arund.");
ift (ount == 1) {
console.log("No botles of beer on the wall.");
}
}
</script>
The counter and bottles variables are both explicitly declared with var
. FatFinger, unlike regular JS, requires explicit declaration because it will assume implicit variables are actually misspelled variables declared earlier.
bottles
is variously written as botles
, bottless
, botttles
. Even built-in JavaScript objects like console
can also be misspelled, here as constole
, connstole
, and conable
. The sloppy work seeps into the strings themselves, so it is not printing "No more bottles of beer on the wall." but actually "No more botles".
To get this script to execute as FatFinger in the html page, we have to first include the fatfinger.js library, and then put our FatFingered code within a script tag with any misspelling of JavaScript (here "javoscript" on line 2).
Behind the scenes, of course, FatFinger needs to turn this into functioning JavaScript that will run in the browser. It does this by finding identifiers that it can't identify and comparing them to a list of everything in scope. It uses Damerau–Levenshtein distance to find the closest match. This is the minimum number of operations (consisting of insertions, deletions or substitutions of a single character, or transposition of two adjacent characters) required to change one word into the other. So each unknown identifier is transformed into the one with the closest-matching name.
While this project had to be written in JavaScript -- the most chaotic of widely used languages -- it caused an issue in that building a list of what's in scope is not so easy. JS doesn't have a fully developed system of reflection -- the ability to read its own metadata and inspect itself -- that Java or C# does. In the buildLocalIdentifiers method we can see how this is done, with all the FIXMEs about scope issues. It begins with this comment:
// FIXME: there no scope and it doesn't even know if a var has been declared yet or not.
// Perhaps capture line numbers where things are declared and use the tree (since at this stage we can generate one) to help with
// scope? However, public/private is tricky in js and there are cases where you can refer to things that are not yet declared
For the moment, these are mostly settled with a warning that "FatFinger has a poor concept of scope, so if you're doing fancy OO stuff, ask yourself: is there a good reason I haven't made everything global??? If not, this might not be the right library / coding style for you."
Comments
Thanks for sharing this fascinating example. I'm adding a few top-level references here for others:
On the project homepage I was particularly interested in the playful point that the language will "Question forty-five years of advice against expressiveness in the text of code" and the link to Dijkstra's "The Humble Programmer" (1972). I wasn't sure which aspects were referenced, but it made me think of how Dijkstra uses both "clever" and, in particular, "baroque," as insults.
I like the idea of FatFinger as a paradigm -- perhaps not just a baroque but an Elizabethan one. It might be interesting, for example, to imagine it as a model for Elizabethan programming that could as a matter of course accommodate things such as a wide variety of spellings of Shakespeare's name, such as Schakespeire, Shakesspere, Shakspeyre, Shakysper, Shaxkspere, Shaxpeer, or Shexpere, with inconsistency even from the author himself.
Have you considered submitting a few examples such as this to Rosetta Code, e.g. their 99 Bottles of Beer task? They already host examples from a number of esoteric languages. While this JavaScript dialect is implemented as a library, not a separate language per se, I imagine such examples would be of great interest.
Yes! The call for less cleverness has become, I believe, the central tenet of code style; that we "respect the intrinsic limitations of the human mind" as Dijkstra puts it -- to make code clear and simple so that we can read code we haven't seen before, or have ourselves written and forgotten.
Programmers have found ways to rebel against this doctrine -- generally in non-production code contexts -- in what I like to call Less Humble Practices: code golf, esolanging, obfuscated code, etc. But in terms of mainstream coding practices, it's still Dijkstra's world.
Cleanly-written code doesn't necessarily reflect clarity of thought. It gives programmers the illusion of control. We (irrational humans) are regularly proven very poor at judging the code's performance. The '60s ideal of writing perfect code has been replaced with bug mitigation (TDD etc). FatFinger is a reaction to this impossible effort to write perfect code, referring to the cycles of compulsive programming in Joseph Weizeinbaum's Science and the Compulsive Programmer. It is harder to be compulsive -- or to believe that you are writing perfectly clean, clearly-written code -- in a system that encourages disorder and accepts misspellings.
So the Elizabethan-style alternate spellings are right in line with the spirit of FatFinger, where specifics matter less than expressing the general gist of a program to the interpreter.
Elizabethan code also brings to mind the Shakespeare esolang -- one that is entirely contrary to code clarity, instead opting to use code to dramatize something usually unrelated to the code's performance (multicoding, as per @nickm and Matteas's "A Box Darkly"). There is also the Esopo project that reacts to it, offering new ways to express oneself poetically in the text of code, without embracing the silliness (or the huge word count) of Shakespeare.
Thanks, I will do that!
On cleverness versus "cleverness", the baroque and the downright rococo: topically, there has recently been (another) call to "write boring Haskell" (a quick search here will provide arguments both for and against) - as a reaction against some of the deeper behaviours the language supports.
The complaint here is not against IOCCC-style frippery, but rather the tendency to dive deeply into levels of abstraction in production code. (For a language that is more explicitly based upon category theory - or "abstract nonsense" as the joke goes - than most, this seems a tendency it's all too easy to succumb to.)
And I think that's a central tension in code development. Programmers have learned that boring code is reliable code, but that doesn't mean they don't want to sometimes use hacky tricks, or experiment at the code level. That's where code golf, esolanging etc, sometimes comes in.
I don't really understand what's happening in the Haskell community, but I found the Boring Haskell Manifesto. First off, I find it reassuring that, while art manifestos have more or less gone away, code development manifestos are still going strong, and are far more sincere than the art manifestos of today. Speaking of which, it reminds me of the OK Art Manifesto, which tells us that "OK artists really want to make great art, they shoot for the stars, but their work ends up being just OK. OK artists are OK with this." Most programmers don't shoot for interesting code and fall short, but set out to write boring code all along; to write something interesting is usually a mistake.
I love the possibilities of this. Codifying a concept of "distance" in a language and then performing closure across such distance is a kind of poetics. It functions not unlike rhyme.
One comparison of distance metrics to the poetic patterning of language is that a pattern such as a rhyme scheme (ABAB) is a set of relations, and those relations are often established through similarity/distance between units which are then understood to be equivalent even when different. So, in Shakespeare's Sonnet 130:
Here "sun / red / dun / head" resolves to ABAB rhyme, making "red / head" equal to BB for the purposes of the scheme, even though the two terms are not identical. By comparison, in FatFinger.JS "botles / botttles" are BB (both are understood to reference
var bottles
) even though these terms are not identical.This led me to a line of inquiry. What are the practical consequences of FatFinger.JS distance for non-fat-fingered applications?
1. near language
Playing around with the web demo, there seems to be a reasonable distance threshold on the number of add-delete-change-swaps (~4). Proximity can be used to playful effect with word choice rather than typos -- for example, in this simple implementation of the Fizz Buzz game / FizzBuzz program:
Here the ten different labels are not miskeyed, but near grammatical forms and near-words. For example, the initial digraph "wh-" in English is associated with interrogatives (question words). The results are less like error correction and more like an exploration at the intersection of natural language programming and obfuscated code, only using a rewriter rather than a generator. The simplified output only contains three variables (ABC):
2. Infinite distance
What if distance-based coding was scoped, and each scope always matched something (at any distance). In theory, this would mean that any unknown would always be taken as the previous declaration:
The terms would would match, because
A
is "the only variable in the room" so to speak -- and thus the addressee or referent. This creates an unrestricted field of language for scopes with one declared variable, a bipartite field for two variables, et cetera. So this would be valid:...as it would resolve to:
The effect would be somewhat like the way in spoken language we may assume the attachment of new referents to priors.
I say "would" throughout these examples because FatFinger.JS does not actually work that way -- at least not completely. It is not a tabula rasa that starts out empty and gets populated by new variable names as they are declared by the coder. Instead, it is already pre-populated with things like the built-in keywords of JavaScript: words like
alert
,document
,name
, et cetera.3. Poetics at the intersection of English and JavaScript
Given that some language API is already present and already exerting a gravitational pull one nearby language, I was curious what the effects were. As an initial test I tried running words from Ogden's Basic English list (~850 words) through FatFinger.js as undeclared variable names in the form
word++;
in order to see if and when the parser would replace them common English words with JavaScript words.The following is a list of the pre-existing synonyms that resolved to JavaScript keywords; a kind of rhyming dictionary for default FatFinger.JS (although some of its effects could be disrupted with each new declaration).
In each entry the keyword is given first, then a list of all the Basic English words that might resolve to it. I was so pleased with the results that I'm naming it.
"In Other Words (Fat Dactyls)"
2020-02-20
alert
confirm
document
history
location
name
navigator
prompt
screen
At this point it is tempting to write a group of simple FatFinger.JS programs in this constrained vocabulary, and I still may. However to me this is already a piece of writing (upon which other writing could be built), and it already suggests insights and at least one conclusion. "In Other Words" is a discovered vocabulary which is poetic in itself. It is not contained anywhere in the source code of FatFinger.JS, but which is nonetheless effected, purely by how an implementation of closing distances causes the English language to interact with JavaScript. This suggests that, given a computed distance or difference and a potential vocabulary, any equivalent coding language or tool also has its own poetic vocabulary: a secret thesaurus waiting to be discovered.