Invitation to J (a jinvitation if you will)

When I first saw J code I assumed it was a toy language mostly created to be deliberately perplexing like many esolangs.  On further investigation I found it is a well thought out and principled language that is worth a look.  What I'd like to show through a small example is that J is what you get if you have a hyper focus on terseness and you push through to logical extremes.

If you haven't seen J code before here is an example of the sort of initially perplexing code you might find:

Fibonacci sequence:

fibN=: (-&2 +&$: -&1)^:(1&<) M."0

Hailstone sequence: (aka Collatz)

hailseq=: -:`(1 3&p.)@.(2&|) ^:(1 ~: ]) ^:a:"0

To give you a sense of what is going on and why such crazy looking code is actually quite well designed, let's take some python code and start applying some principles of brevity and see where we get.

We'll take brevity to mean removing everything that could just as well be inferred from context.

Here is some python code for calculating the average of a list of numbers

def average(values):
    total = 0 
    for v in values:
        total += v
    length = len(values)
    return total/length

We can run it as

>>> average([1,2,3,4])
2.5
One way to shorten things would be to just abbreviate all the names used.  That's fine though sort of a cheap trick.  We'll save that for the end if needed.  Instead let's first use the built in "sum" function

def average(values):
    total = sum(values) 
    length = len(values)
    return total/length
We can also get rid of the temporary values and just do the math in situ

def average(values):
    return sum(values)/len(values)

At this point it might occur to you, why even define a function?  We could just do the averaging calculation in place whenever we need.  

sum(values) / len(values)

But now you are getting into the zone and you see that we have to repeat "values" twice.  What if we made a rule that 

- putting function names together just automatically repeats the input values

In other words lets say:

(sum / len) values

is just a short hand for

sum(values) / len(values)

Maybe that seems like a leap but we are aiming towards brevity.  If we want to make a rule that removes a redundancy then we can do that in any way we might like.  It turns out that jamming three functions together into one function sort of behavior comes up a lot.

As a quick aside we note that these three function being jammed together have a certain structure:

"sum" takes a list as input

"/" takes a left and right input

"len" takes a list as input

So our little shortcut of  (functionA functionB functionC) will only be allowed for functions that follow this sort of symmetrical pattern (list function, left-right function, list function).

So now we have 

(sum / len) values

Another quick aside: let's make a very concise sum function.

"sum" is really just a pattern for shoving "+"s between all the elements of a list:

sum([1,2,3]) 

is just a way of writing

1 + 2 + 3

Taking an operator and shoving it between items of a list turns out to be a very common operation, so lets give it a short expression.

Keeping things minimal we can represent "sum" as "+/"  where the "/" says make a variation of the function "+" that knows to shove itself between all the items in a list (this is a "reduce" for those of you playing our home game).

Of course if we let "/" have this meaning then we'll likely confuse it with "/' meaning divide so let's get creative and use "%" to mean divide (sort of looks like a fraction). 

Assuming that our hypothetical system is smart enough to see "+/" as a single unit we now have

(+/ % len) values    NB.  +/ is a shortcut for sum, % is our new / and len is len

"len" is already naturally short but we can do better.  Let's use "#" (number sign) for counting things.

This yields 

(+/ % #) values

Do we really need spaces?

(+/%#)values

Now we have an expression that is is literally shorter than even the name of our original function "average".

Aesthetically you might be thinking this is pretty bad and not worth the effort and not maintainable.  It's certainly a different look.  The main thing is that each decision made sense and is learnable. 

There is much more to it than this but this gives you a taste of the thinking going on in this language.  The thought experiment now is to ponder what programming is like if multiline algorithms can now be represented with a few characters.  This is actually a bit of a toy example.  The compression rate is typically greater for more serious algorithms.

If you are interested in learning more the Learning J Book is a nice place to start.  The J website also has lots of resources.  J is a fun language and has many (mostly pleasant) surprises.

Comments