J one-page interpreter fragment (1992)

5 years ago (code.jsoftware.com)

Its density is many times higher than most C programs, but that's no big obstacle to understanding if you don't attempt to "skim" it; you need to read it character-by-character from top to bottom. It starts off defining some basic types, C for Character and I for Integer, and then the all-important Array. This is followed by some more shorthand for printf, return, and functions of one and two arguments, all of the array type. The DO macro is used to make iteration more concise.

Then the function definitions begin. ma allocates an array of n integers (this code is hardcoded for a 32-bit system), mv is basically memcpy, tr (Total Rank?) is used to compute the total number of elements, and ga (Get/Generate Array) allocates an array.

This is followed by the definitions of all the primitive operations (interestingly, find is empty), a few more globals, and then the main evaluator body. Lastly, main contains the REPL.

While I don't think this style is suitable for most programmers, it's unfortunate that the industry seems to have gone towards the other extreme.

  • Btw, this is considered more or less canonical k style. (with q being a slighty more readable version of this)

    It takes some time getting used to it, but, once you get the hang of it, going back to e.g. Python/Pandas is painful.

While this example is pretty dense, a "one page programming language interpreter" doesn't need to be so impenetrable. Here's a one-page (67 line) interpreter for a subset of the Jsonnet programming language, implemented in Scala:

- https://github.com/handsonscala/handsonscala/blob/v1/example...

From the top down, that snippet contains an AST definition, a parser, an object model, an evaluator, and an output serializer. This 67-line programming language interpreter is one of the projects in my book https://www.handsonscala.com/ (chapter 20), and while this example is minimal the same techniques were used for my production-ready Sjsonnet interpreter

Some time ago I've made a humble decoding of this wonderful code snippet: https://zserge.com/posts/j/

I want to point out the large part of "unreadability" feeling here comes from the fact the code is pre-ANSI C89. so tr(r,d)I d;{I z=1;DO(r,z=zd[i]);R z;} is just a function definition.

I would say the structurally this extremely easy to read considering there is zero comments and the way it is presented. One can make this readability obvious by just expand the macros, change indentations and add line breaks, or, you can just spend several days to "get used" to it like Roger Hui (and Arthur who just use this kind of style for life).

The repository[0] for J is just as unreadable (albeit with slightly more white space). I don’t know how a project is managed like this. Even the file names look obfuscated.

[0]: https://github.com/tavmem/j

  • > The repository[0] for J is just as unreadable

    People who can read this find it readable.

    I think it's a mistake to think that because you can't read the code, that it is the code that is somehow unreadable, instead of a language you simply haven't learned to read.

    > I don’t know how a project is managed like this.

    It's not dissimilar to working with any proprietary programming language: You have to learn how to read and write this language.

    The fact that we can use an existing language's compiler can be confusing to people who don't know that language very well, but if you approach it from the perspective of a new (proprietary) language, using an existing language's compiler can be a great way to leverage the benefits of that existing language.

    • > I think it's a mistake to think that because you can't read the code, that it is the code that is somehow unreadable, instead of a language you simply haven't learned to read.

      This argument is significantly weakened when simply removing the meaningless macros and adding whitespace improves readability.

      It's not like it is complicated because it is written in a structurally different language, nor will you actually leverage any benefit from learning this language. It's just obfuscated under the guise of abbreviation.

      6 replies →

    • >People who can read this find it readable.

      > I think it's a mistake to think that because you can't read the code, that it is the code that is somehow unreadable, instead of a language you simply haven't learned to read.

      The same could be said about Brainfuck. With enough effort people can learn to read almost anything; that doesn’t mean that everything is equally readable.

      1 reply →

  • I think this is the main J repo [0]. (Though I’m not sure if it’s any more readable.) Possibly interesting that Tavmem is the maintainer of Kona (open source K3/4). I guess he’s comfortable with all APL/J/K languages.

    [0] https://github.com/jsoftware/jsource

What people often miss with J is how good it is for exploratory programming due to its terseness and most importantly its integrated columnar store. It replaced R for data wrangling in my stats programs, and relegated it to just running the models.

Once you get used to it, it's really a breeze and far easier to modify than 500+ sloc R scripts.

Of course, that does not work for everyone, and especially not for big teams, but still!

  • > It replaced R for data wrangling in my stats programs

    Do you find it faster?

    I have good experiences with J due to its terseness, but I'm curious why scientists still use R/Python - is it just inertia? Libraries/FFI?

    • Faster to program yes, massively so. Faster to execute, it depends on what I'm doing, but it's never a problem for me as my data is usually pretty small and quite messy. It doesn't feel slow at all, at least. And obviously if you're doing arrays you'll certainly not run faster in Python/R.

      As for R/Python it's mostly familiarity with the notation (especially for python) and established popularity, with a large ecosystem as a consequence.

      I mean, as a beginner in Python it just works (slowly). As a beginner in J, you cry... The interesting distinction is that Python/R APIs can be quite convoluted and the rug may be pulled from under you without warning, while in J you learn the primitives and you're off to the races. Also, J is much faster for its use case and avoids the need to write C in most cases where using it is relevant.

Somehow still more readable than actual J code

  • Unlikely. The point of writing C in this manner was to bring C closer to APL - or J, which was an evolution of APL at the time. So that J interpreter would be written in a form of J language. J language itself doesn't need to satisfy also C syntax, so J code is somewhat easier.

I actually used this as inspiration for a little self-challenge to write a programming language in 24hrs not too long ago [1]. I didn't get quite as far as I'd've liked to, but nonetheless if you have some time to kill, it was an extremely fun task to tackle.

[1] https://github.com/block8437/afternoon-lang

Question: where/how are the `mv` and `pr` functions defined? And how does this even compile when it's using `gets` without `#include <stdio.h>` ?

I do see it's using the `implicit int` rule though.

  • `mv` is defined on the same line as `ma` is, right after the five `#define`s.

    `pr` is on the last line before the only line break.

    It's a pretty dense program to walk through, but not as completely impenetrable as I first thought after taking some time and making notes as I went.

Is there any Lisp interpretation on J language? I have hard time searching anything called with one letter only.

  • Anyways, info about J can pretty much be found only on its website (jsoftware.com), so don't really bother looking elsewhere. The only other good place is Rosettacode.

    What do you mean by "Lisp interpretation on J language"?

    There's an APL in Common Lisp called April. Otherwise, there's an unfinished J interpreter on the Racket package manager.

    • Sorry for incorrectness, I want to look at interpreter of any dialect of Lisp language, written in J, if the one exists. I found J's approach to syntax interesting to explore.

      2 replies →

It leaks memory on every line it evaluates.

  • I guess freeing all those pointers returned by ma() would be too verbose.

    Seriously though, this must have been a deliberate design decision? Arthur Whitney is no amateur.

  • I'm sure memory management was left to deal with later. In Roger Hui variant, memory leaks were eliminated early on, on a systemic level.