New to J Reference Wiki YouTube RosettaCode ArrayCast GitHub Forum Stack Overflow Search Everything
0 Items
0 Items
0 Items
0 Items
0 Items
0 Items
0 Items
0 Items
J Forum Posts Since 1 January 2024
0 Items
0 Items
0 Items
0 Items

A Very Brief Introduction to J

J is one of the array languages. It's a direct descendant of a language developed in the 1960s at IBM called APL, whose inventor, Ken Iverson, had what turned out to be profound ideas on the power of notation as a tool of thought.

J's primitive operators act on n-dimensional arrays, not (just) on scalars. As such, J programs tend to be much shorter than conventional programs. Here's a line of J code to add the elements of two vectors (note the absence of a loop):

Conventional programs typically start with arrays, use (often nested) loops to decompose them into scalars, operate on the scalars, then combine the individual scalar results into arrays to produce an output.

By contrast, J programs typically start with arrays and operate on them as arrays—no decomposition, no explicit scalar operations. Here's how you'd use the / ("insert") operator to interleave + between the elements of a vector and thereby sum them. Note that there is no loop, no index, and no accumulation variable:

J has a lot more primitives than a conventional language. This is actually a mixed blessing: J's primitive set is so large (~150) that it takes longer to become comfortable with J than it does to become comfortable with, for example, Python (most of whose ~40 primitives you probably already know from other languages).

But combining its many array operators gives J programmers dizzying leverage over their data. A casual line of J can often accomplish as much as a page or two of laboriously-constructed conventional code.

It's not all good news, unfortunately: because the primitives are doing so much under the covers, reading J code takes a lot of practice. Most developers become skilled J writers well before they become skilled J readers.

On the other hand, a J programmer will write (and read and debug and support) much less code than other programmers to achieve the same results. This makes J a highly-productive language particularly well-suited to rapid prototyping.

More good news: because J operators act on such rich data structures, the J runtime has a lot of scope for "free" optimizations—for example, the contents of frequently-searched arrays may be automatically indexed to improve performance.

There are several instruction manuals for J (linked above). Have a look at them if you're interested in learning more—they take various approaches to teaching the language and you may resonate to one more than the others.

The full collection of instructional materials—whose sections are listed at right—can be searched (by Phrase, Words or section Title) for text or code, so it's easy to get multiple explanations of tricky concepts. Further, the J Forum archive as well as numerous other J resources (Stack Overflow, RosettaCode, the J Wiki and others) are searchable through the other tabs on this site. They're excellent sources of examples, clarifications and detailed discussions.

Below is a simple J interpreter for trying out code. For layout convenience, it puts the most recent result on top. You can use it to work along with whichever manual you choose. When you're ready, the J Playground (linked above) is a web-based J IDE for doing more advanced development. And, of course, you can install the full J IDE on your local machine.

A Little More J (a Pre-Tutorial)

(This section is under construction.)

To whet your appetite, here are a few J expressions you can try out. Just click one to paste it into the interpreter, then press Enter to execute it. And, of course, you should feel free to experiment by tweaking the expressions just to see what happens.

Because it operates on whole arrays at once, J code doesn't tend to have loops. It still, however, needs counters and indexes and offsets and the other numbers we normally associate with loops. These are supplied by the i. ("integers") verb, which produces an array of integers.

Let's move to the natural numbers (1, 2, 3, ...) with the "increment" verb >: and then do some loopy things with i. .

Unlike most source code, J is interpreted right to left. This seems unnatural (it is unnatural) and, further, J's large primitive count makes it impossible to remember a set of verb precedence rules (* before + before [], for example, or NOT before AND but AND before OR).

J sidesteps the issue by having no verb precedence rules at all—verb precedence marches from the end of the line to the beginning without regard to which verbs appear. This is both disorienting and a profound relief. We'll see it more clearly as we go.

/ is called an "adverb" because it's attached to—and changes the meaning of—a verb, in this case +. In the same way I can walk (verb) quickly (adverb), I can + (verb) / (adverb). (For familiarity's sake, J borrows a few terms from English grammar, which is ironic in that few Americans are familiar with them.)

Adverbs change the way verbs operate on arrays. The adverb / inserts its verb between an array's components. If the array is a vector (a 1-dimensional array), it inserts the verb between the elements. If the array is a table (a 2-dimensional array), it inserts the verb between the rows…and so on up to 64-dimensional arrays—J's limit—but we won't go there.

We've looked at vectors (1-D). Now let's see how J deals with tables (2-D). If you give i. two numbers, it'll produce a 2-D array.

.tfel ot thgir daer ot rebmemeR

The , ("append") verb concatenates two arrays.

Note that we had to enclose i. 4 in parentheses. Otherwise the 4 would have been prepended to 100 200 300 400, and those five numbers would have been fed to i. resulting in a 5-dimensional array too large for available memory. (Again, read right to left.)

The < ("box") verb encloses its operand and is useful when you want to mix heterogeneous data, like characters and numbers or multiple arrays of various shapes. It's also useful when you're learning about new adverbs, which is what we'll do below. But, first, some boxing examples:

We've seen the / ("insert") adverb. Now let's look at a few more adverbs that help to explain why J doesn't typically need loops.

The \ ("prefix") adverb creates progressively longer prefixes of its operand's components. To illustrate, we apply < to each of the prefixes of i.6:

\ applied the < verb to 0, then to 0 1, then to 0 1 2, and so on. Then it assembled each intermediate boxed result into a single result array of boxes.

\ lets us easily calculate intermediate sums, another common thing to do with loops.

This example needs some explanation. When you apply an adverb (like /) to a verb (like +), that actually creates a new verb (+/). And you can apply another adverb (like \) to that new verb creating yet another verb (+/\), which can then be applied to an operand (i.6). The effect in this case was to sum (+/) each of the prefixes (\) of i.6.

What if you wanted to sum the prefixes of i.6 in reverse? That is, 5, 5 + 4, 5 + 4 + 3, ...? (In conventional programming terms, a descending loop.) You could use the |. ("reverse") verb to reverse i.6, then apply +/\ to it.

Using < ("box") as the verb instead of +/ illustrates what's happening.

Another adverb, \. ("suffix"), creates progressively longer suffixes of its operand's components. To illustrate, we apply < to each of the suffixes of i.6:

Most verbs have two meanings: one meaning for a single operand (the "monadic" meaning) and a more-or-less related meaning for two operands (the "dyadic" meaning). (People who hate J find this ambiguity to be one of their favorite things to hate.)

So—as in most conventional languages—the - verb's monadic meaning is "negate," while its dyadic meaning is "subtract."

Surprise! Because - is a verb, J's negative number syntax uses _ not - . (I'm trying to keep the arbitrary-seeming weird to a minimum, but it's going to seep in occasionally. Try to roll with it.)

The central problem a J programmer faces is how to decompose and operate upon arrays without using loops and scalar operators. Part of the answer is supplied by adverbs like \ and / and \. , which take apart arrays in various ways and apply their verbs to the parts, then assemble the new parts into a single array result.

Another, quite large part of the decomposition picture is supplied by the " (or "rank"…as in "rank and file") conjunction.

Conjunctions (in grammar terms, think of "and" or "or") bind to two primitives to produce (often) a new verb. They're the counterpart of adverbs, which bind to a single verb to create a new verb.

Arrays are said to have "rank," which is just an indication of how many dimensions they have. Rank-0 arrays are scalars (like 47). Rank-1 arrays are vectors (i. 5). Rank-2 arrays are tables (i.5 6). Rank-3 arrays are oblongs (i. 5 6 7). And so on up to rank-64, though unless you're doing work with images or 3D graphics (common uses of J), you'll seldom find yourself past rank-2.

The rank conjunction " glues a verb to a number (or two, but that comes later), and that number controls how the verb is applied to its operand(s). For full technical explanations, search Titles for "rank"—I'm going to give you a rough-and-ready explanation you can use to solve problems.

...

The rank conjunction is a powerful array "decomposer" and it actually generalizes nicely to negative numbers, but for that you'll have to search the manuals (try Phrase: negative rank)—I've never used it in 30 years and will not inflict it on you now.