Beginner

Control Flow in J

Learn control flow in J — conditionals, select/case, while and for loops, and the array-oriented and tacit alternatives that often replace them

Most languages treat control flow as the backbone of a program: you branch with if, you repeat with for and while, and you walk through data one element at a time. J supports all of these structures, but it also invites you to question whether you need them at all.

As an array-oriented language, J’s verbs apply to entire arrays at once. An operation you might write as a loop in Python—summing a list, filtering even numbers, transforming each element—usually collapses into a single expression with no explicit iteration. Loops still exist for genuinely sequential logic (accumulating state, early exit, stepwise algorithms), and J provides a full set of control words inside explicit definitions. But the idiomatic J solution often replaces a loop with an array operation or a tacit (point-free) conditional.

This tutorial covers both worlds. First the explicit control structures—if./elseif./else., select./case., while., and for.—and then the array-oriented and tacit techniques that frequently make them unnecessary. Along the way you’ll see why experienced J programmers reach for loops far less often than they did in other languages.

Conditionals: if. elseif. else. end.

J’s branching keywords end with a period and live inside an explicit definition. The form 3 : 0 begins a verb whose right argument is y; the body runs until a line containing only ).

Create a file named conditionals.ijs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
NB. Classify a number using if. / elseif. / else.
classify =: 3 : 0
if. y < 0 do.
  'negative'
elseif. y = 0 do.
  'zero'
else.
  'positive'
end.
)

echo classify _5
echo classify 0
echo classify 42

Each branch is guarded by a condition followed by do., and the whole structure closes with end.. The value of the last expression in a branch becomes the verb’s result. Note J’s _5 for negative five—the underscore is the negative sign, distinct from the - verb.

Multi-way branching: select. case. end.

When you are comparing one value against several fixed alternatives, select./case. reads more cleanly than a chain of elseif.. A bare case. with no value (just case. do.) acts as the default.

Create a file named select.ijs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
NB. Map a number to a word using select. / case.
letter =: 3 : 0
select. y
  case. 1 do. 'one'
  case. 2 do. 'two'
  case. 3 do. 'three'
  case. do. 'many'
end.
)

echo letter 1
echo letter 2
echo letter 3
echo letter 9

The argument after select. is evaluated once and compared against each case. value. The first match runs, and the trailing default case. catches everything else.

Loops: while. and for.

For genuinely sequential work—where each step depends on the result of the last—J offers while. and for. loops. Inside an explicit definition, =. makes a local assignment, which is what you want for loop counters and accumulators.

Create a file named loops.ijs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NB. while. loop — compute a factorial step by step
factorial =: 3 : 0
n =. y
acc =. 1
while. n > 1 do.
  acc =. acc * n
  n =. n - 1
end.
acc
)

NB. for_i. loop with continue. — sum the even numbers below y
sumeven =: 3 : 0
total =. 0
for_i. i. y do.
  if. 1 = 2 | i do. continue. end.
  total =. total + i
end.
total
)

echo factorial 5
echo sumeven 10

The for_i. form binds each element of i. y (the integers 0 through y-1) to the loop variable i. The continue. word skips the rest of the current iteration—here we skip odd numbers, since 2 | i is the remainder of i divided by 2. J also provides break. to exit a loop early and return. to leave the verb entirely.

The array-oriented and tacit way

Here is the part that makes J different. The factorial, the sum, and the filter above can all be written without any loop or conditional at all, because J’s verbs already operate over whole arrays.

Create a file named array_control.ijs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
NB. Sum 0..9 — no loop, just "plus over" (+/)
echo +/ i. 10

NB. Factorial of 5 — the ! verb, no loop
echo ! 5

NB. Keep only the even numbers — filter without an if inside a loop
echo (#~ 0 = 2&|) i. 10

NB. Tacit conditional with Agenda (@.)
NB. (* y) is the sign: _1, 0, or 1; adding 1 gives an index 0, 1, or 2
classify =: ('negative'`'zero'`'positive') @. (1 + *)
echo classify _7
echo classify 0
echo classify 100

A few things are happening here:

  • +/ i. 10 inserts + between the elements of 0 1 2 ... 9, summing them—this replaces a for loop with an accumulator.
  • ! 5 is the factorial verb; the loop in loops.ijs was reproducing a built-in.
  • (#~ 0 = 2&|) is a tacit filter: 2&| gives each element mod 2, 0 = ... marks the even ones, and #~ keeps exactly those—the array equivalent of “loop and if.”
  • @. (Agenda) is J’s tacit conditional. It selects one verb from a list based on an index. The selector 1 + * turns a number’s sign (_1, 0, 1) into an index (0, 1, 2) that picks the matching word.

The lesson is not that loops are forbidden—it’s that in J, “iterate over an array” is usually a property of the verb, not something you spell out. Reach for explicit control flow when the logic is truly sequential or stateful; reach for array operations and @. for everything else.

Running with Docker

Run each example with the official J image. The container’s entrypoint executes the .ijs file you pass it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Pull the official J image
docker pull nesachirou/jlang

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

# Run the select/case example
docker run --rm -v $(pwd):/app -w /app nesachirou/jlang select.ijs

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

# Run the array-oriented example
docker run --rm -v $(pwd):/app -w /app nesachirou/jlang array_control.ijs

Expected Output

conditionals.ijs:

negative
zero
positive

select.ijs:

one
two
three
many

loops.ijs:

120
20

array_control.ijs:

45
120
0 2 4 6 8
negative
zero
positive

Key Concepts

  • Control words end in a period: if. do. elseif. do. else. end., select. case. do. end., while. do. end., and for_i. do. end. are J’s structured control keywords.
  • Conditionals live in explicit definitions: control structures run inside a verb defined with 3 : 0 (or verb define), where y is the argument and =. makes local assignments.
  • select./case. for multi-way branching: cleaner than chained elseif., with a bare case. do. serving as the default branch.
  • continue., break., and return. give fine-grained loop and verb control when you need it.
  • Array operations replace loops: verbs like +/ (sum), ! (factorial), and #~ (filter) act on whole arrays, so most “loops” in J are a single expression.
  • @. (Agenda) is the tacit conditional: it selects a verb by index, letting you branch without naming arguments or writing if..
  • Think in arrays first: in J, reserve explicit loops for genuinely sequential, stateful logic; prefer array operations and tacit conditionals for everything else.

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