Beginner

Operators in J

Explore arithmetic, comparison, boolean, and array operators in J — and discover how reduce, fork, and tacit composition replace traditional control flow.

In most programming languages, “operators” are a small, fixed set of infix symbols like +, -, and ==. In J, what other languages call operators are called verbs, and they are first-class citizens of the language. A J verb works on scalars and entire arrays with equal grace — 2 + 3 and 2 + 1 2 3 4 5 are equally idiomatic, and neither one needs a loop.

Because J is array-oriented and tacit, operators are also the primary tool for control flow. Reducing a list with +/ replaces a for loop that accumulates a sum. Filtering with # replaces an if inside that loop. Forks and hooks let you compose verbs into new verbs without ever naming an argument. This page introduces the core operator set and then shows how J builds bigger ideas out of small, dense pieces.

A few syntactic reminders that change how operators behave compared to mainstream languages:

  • J evaluates expressions right to left with no operator precedence. 2 * 3 + 4 is 14, not 10.
  • Many symbols are overloaded between monadic (one argument) and dyadic (two argument) forms. - subtracts when given two arguments and negates when given one.
  • Negative literals use _, not -. _3 is negative three; - 3 is the verb - applied to 3.

Arithmetic and Comparison Verbs

Create a file named operators.ijs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
NB. -- Arithmetic verbs --
echo '-- Arithmetic --'
echo 2 + 3                  NB. addition
echo 10 - 4                 NB. subtraction
echo 6 * 7                  NB. multiplication
echo 10 % 4                 NB. division (note: % not /)
echo 2 ^ 8                  NB. power (2 to the 8th)
echo 3 | 10                 NB. residue: 10 mod 3

NB. -- Monadic forms of the same symbols --
echo '-- Monadic --'
echo - 5                    NB. negate
echo % 4                    NB. reciprocal (1/4)
echo | _7                   NB. absolute value

NB. -- Comparison verbs (return 0 or 1) --
echo '-- Comparison --'
echo 5 = 5                  NB. equal
echo 5 ~: 3                 NB. not equal
echo 3 < 7                  NB. less than
echo 7 >: 7                 NB. greater than or equal

NB. -- Boolean verbs --
echo '-- Boolean --'
echo 1 *. 0                 NB. AND
echo 1 +. 0                 NB. OR
echo -. 1                   NB. NOT (monadic)

NB. -- Array extension: operators work element-wise --
echo '-- Arrays --'
echo 1 2 3 + 10 20 30
echo 2 * 1 2 3 4 5
echo 1 2 3 4 = 1 0 3 4

NB. -- Reduce adverb / : insert a verb between items --
echo '-- Reduce --'
echo +/ 1 2 3 4 5           NB. sum
echo */ 1 2 3 4 5           NB. product
echo >./ 3 1 4 1 5 9 2 6    NB. maximum
echo <./ 3 1 4 1 5 9 2 6    NB. minimum

NB. -- Right-to-left evaluation, no precedence --
echo '-- Precedence --'
echo 2 * 3 + 4              NB. 2 * (3 + 4) = 14
echo 10 - 3 - 2             NB. 10 - (3 - 2) = 9

A few things to notice. Division uses % because / is reserved for the reduce adverb. The verb | is residue with x | y meaning y mod x — the left argument is the divisor, which surprises newcomers from C-family languages. Comparison verbs return 0 or 1, not a separate boolean type; that means you can do arithmetic on the results of comparisons, which is one of the secrets behind J’s terse expressions.

The reduce adverb / is where J starts to feel different. +/ 1 2 3 4 5 doesn’t loop — it inserts + between every adjacent pair: 1 + 2 + 3 + 4 + 5. The same pattern works for any verb: */ for product, >./ for max, <./ for min, *./ for “all true,” +./ for “any true.”

Tacit Composition: Building Verbs from Verbs

J’s most distinctive feature is tacit programming: defining new verbs by composing existing verbs, without ever referring to the arguments by name. The two core composition patterns are the fork (three verbs in a row) and the hook (two verbs in a row).

A fork (f g h) applied to y means (f y) g (h y). So (+/ % #) applied to a list means “(sum of list) divided by (count of list)” — the arithmetic mean.

Create a file named tacit.ijs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
NB. Tacit verbs: define new operators by composing existing ones.

NB. Fork (f g h) y -> (f y) g (h y)
mean =: +/ % #
echo 'mean of 1..5:'
echo mean 1 2 3 4 5         NB. (1+2+3+4+5) % 5 = 3

NB. Another fork: range = max - min
range =: >./ - <./
echo 'range of 3 1 4 1 5 9 2 6:'
echo range 3 1 4 1 5 9 2 6  NB. 9 - 1 = 8

NB. Reflexive adverb ~ : (f~) y -> y f y
square =: *~
echo 'square of 7:'
echo square 7               NB. 7 * 7 = 49

NB. Compose with the @: conjunction
sum_of_squares =: +/ @: *:
echo 'sum of squares of 1..4:'
echo sum_of_squares 1 2 3 4 NB. 1 + 4 + 9 + 16 = 30

NB. Assignment operators: =: is global, =. is local
NB. Update in place by reassigning
counter =: 0
counter =: counter + 1
counter =: counter + 1
counter =: counter + 1
echo 'counter after three increments:'
echo counter                NB. 3

The line mean =: +/ % # reads like a mathematical definition: “mean is sum divided by count.” There is no argument named anywhere — the fork pattern wires the arguments through automatically. This is the same idea as function composition in Haskell, but pushed further: even basic arithmetic combinations like (max - min) are written without lambdas.

The ~ adverb is reflexive: it makes a dyadic verb act on the same value twice. *~ 7 becomes 7 * 7. The conjunction @: is compose: +/ @: *: first squares every item (*: is the verb “square”) and then sums them.

J has two assignment verbs: =: is global (the binding persists across the program), and =. is local (visible only inside the current verb definition). They are verbs themselves, which is why expressions like counter =: counter + 1 look symmetric — you really are applying a verb to counter and counter + 1.

Running with Docker

1
2
3
4
5
6
7
8
# Pull the official image
docker pull nesachirou/jlang:latest

# Run the basic operators script
docker run --rm -v $(pwd):/app -w /app nesachirou/jlang operators.ijs

# Run the tacit composition script
docker run --rm -v $(pwd):/app -w /app nesachirou/jlang tacit.ijs

J scripts (.ijs files) are interpreted line by line. Unlike interactive mode, scripts do not auto-display the value of an expression — you have to explicitly echo (or smoutput) anything you want printed.

Expected Output

Running operators.ijs:

-- Arithmetic --
5
6
42
2.5
256
1
-- Monadic --
_5
0.25
7
-- Comparison --
1
1
1
1
-- Boolean --
0
1
0
-- Arrays --
11 22 33
2 4 6 8 10
1 0 1 1
-- Reduce --
15
120
9
1
-- Precedence --
14
9

Running tacit.ijs:

mean of 1..5:
3
range of 3 1 4 1 5 9 2 6:
8
square of 7:
49
sum of squares of 1..4:
30
counter after three increments:
3

Note _5 rather than -5 — J writes negative numbers with a leading underscore so that the minus sign can be unambiguously interpreted as the verb - (subtract/negate).

Key Concepts

  • Verbs replace operators. What other languages call operators, J calls verbs. They are first-class and can be assigned to names, composed, and modified by adverbs.
  • Scalar extension is automatic. Any verb that works on numbers works element-wise on arrays of any rank. No looping required.
  • Right-to-left, no precedence. 2 * 3 + 4 is 14. Parentheses override grouping; nothing else does.
  • Monadic vs dyadic overloading. Most symbols mean different things with one argument versus two. - negates monadically, subtracts dyadically; % reciprocates monadically, divides dyadically.
  • / is the reduce adverb. +/, */, >./, <./, *./, +./ cover sum, product, max, min, all, any. The same pattern works with any verb.
  • Tacit verbs compose without arguments. A fork (f g h) applies the outer verb g between two inner applications; this lets you write definitions like mean =: +/ % # that look mathematical, not procedural.
  • % is divide, not modulo. Modulo is | with the divisor on the left: 3 | 10 is 1.
  • _ is the negative sign. _3 is the literal negative three. -3 is the verb - applied to 3, which happens to give the same value but means something different.

Running Today

All examples can be run using Docker:

docker pull nesachirou/jlang:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining