Stages of Denial

It is a lazy Tuesday afternoon. Perhaps you were idly browsing your favorite news aggregator, or visiting The Orange Website to wade through murky startup groupthink in search of interesting links.

Today, though, you arrived at an enigma. A proprietary, closed-source, commercial programming language. A bizarre anachronism. The programs, if you can call them that, resemble the results of a prolonged fistfight with a keyboard. The language is simply called “K”. You snort at the vulgar presumptuousness of a single-letter name, before self-consciously averting your eyes from the C compiler output in a nearby terminal.

One K example looks like this:


Completely illegible. Fortunately, another soul has provided a translation into something intuitive and natural:

int sum = 0;
for (int i = 0; i < 100; i++) {
	sum += i;

There is no visible correspondence between the structure of these programs. You discern no parts of speech in the K, no names to grasp onto to extract meaning. Frustrating, and yet… intriguing. K seems to be one of these “code golf” languages designed for writing cryptic one-liners that minimize (key)strokes and demonstrate one’s cleverness. Who doesn’t like to demonstrate their own cleverness? Then you scowl and remember that clever code is impossible to maintain.

Before a treatise can work its way out of your fingers on the value of simple code which is comprehensible to even the most junior Stanford-educated Google employee, (optimization being the root of all evil, as you once read, paraphrased) someone else points out that a more direct translation into another language might look like:

sum = range(100).reduce(plus, 0)

The correspondence is clearer now. If each punctuation character in the K is a separate part of speech, and you reverse their order…

 +      /     !   100
plus reduce range 100

Backwards! You stifle a chuckle. What next? Still dubious, but increasingly invested in this puzzle, you spend some time reading more about K. A few rogue lunatics have furnished mostly-working open source interpreters for dialects of the language, so you give them a try. You read a few essays and tutorials, flip through the manual for K2 that serves as the primary comprehensive reference for the language, and have a go at solving some Project Euler problems.

In the course of your reading you briefly encounter J and APL, which seem even more alien than K, and yet… the same in many ways. J and APL have K’s right-to-left evaluation (in annoying defiance of PEMDAS, for some reason), the / symbol means “reduce”, and you can add 5 to an entire matrix at once as easily as a single number. Why you would want this property remains elusive- it seems rather academic and mathy- but you can’t help but admit that it’s rather neat. You have a growing impression that K is not simply a one-off oddity, but rather a member of a whole family tree.

Fine, K has some appeal. Being able to dash off ,/ to flatten a list is nice. The “grade” operators < and > seemed like a clumsy way to sort at first, but you’re starting to see that “permutation vectors” have many other uses. There’s something deeply right about how list indexing and function application are the same operation. K seems too domain-specific to use for real work, but maybe it would be handy to implement some of the K primitives as a library for your favorite language?

The symbols still feel like a problem. Wouldn’t it be clearer and more readable to simply use spelled-out names? Maybe instead of writing:


You could factor the definition apart and use more explicit syntax:

groupIndices: {[list]
	: =list
countEach: {[dict]
	: #:'dict
maxIndex: {[list]
	: *>list
mostCommon: {[list]
	: maxIndex[countEach[groupIndices[list]]]

In principle, this should be more readable and easier to understand. You have dutifully broken down the definition into reusable parts and given them clear, meaningful names, a best-practice that you learned in your earliest programming courses and have carried forward throughout your professional career. Still, this time there’s something worryingly unsatisfying about the results. The name “mostCommon” is longer than the entire definition in K if you write it more succinctly.

It is possible to write K as if it were a strange-looking Lisp, M-Expressions and all:

fibs: {[n]

Or you could express the same idea without conditionals, recursion, or any explicitly named variables:

{x#x{x,+/-2#x}/0 1}

Does giving a K idiom a name make it clearer, or does it obscure what is actually happening?

sum:     +/
raze:    ,/
ordinal: <<

The word “ordinal” can mean anything, but the composition << has exactly one meaning. (That one took a while to click, but it was satisfying when it did.) If the primitive “group” was just that word to begin with, would you have known what it did any more than if you saw the symbol =?

square: {[number] number*number}
square: {[n] n*n}
square: {x*x}

Sometimes a long name is more descriptive, but sometimes it’s just… longer.

You feel a perverse urge:


It’s more explicit than +/!100, but now it leaves a bad taste in your mouth.

Your old languages don’t feel quite the same.

Reviewing a patch one of your coworkers submitted today, you recognize a loop with a familiar structure:

let max = list[0];
for (let i = 0; i < list.length; i++) {
	if (list[i] > max) { max = list[i]; }

You suggest an alternative, wincing slightly at the lambda notation you need to avoid running afoul of JavaScript’s variadic Math.max():


Your suggestion is rebuffed as cryptic and overly complex. Lambdas are hard to understand, not to mention reduce(). A for loop is much more intuitive, natural, and readable, it is argued. If you used clever tricks everwhere in the codebase, it would be impossible to maintain. A compromise is committed:

let max = list[0];
for (let i = 0; i < list.length; i++) {
	max = Math.max(list[i], max);

On The Orange Website, someone has started a thread about an obscure language with a single-letter name. Commenters are quick to express incredulity at how unreadable it is.

Leaning back in your chair, you think to yourself: