Some FinnAPL idioms in X_eTaL
historical APL idioms re-expressed as typed, extensible array programs
Table of Contents
- Setup
- Reduction: sum
- Reduction: maximum and minimum
- Scan: cumulative sum
- Grade and sorting
- Descending sort
- Boolean positions
- Unique items / nub
- Multiplication table / outer product
- Equality table / identity matrix
- Dot product
- Matrix product
- Sum of squares
- Mean as a train
- Whole-array equality
- All and any
- Even numbers
- Reverse and rotate
- Transpose
- Range / consecutive integers
- Number of elements and shape
- Zero array by scalar extension
- Selection by a mask
- What should not be translated literally
- Classification for the larger corpus
- Reproducing the document
The FinnAPL idiom library is a collection of more than 700 one-line APL idioms, first presented at the 1984 APL conference in Helsinki. This document does not transliterate APL glyph-for-glyph. It asks a more useful question: what is the corresponding X_eTaL way of thinking?
Each example keeps the original APL expression beside executable
X_eTaL. The X_eTaL blocks are ordinary ob-xetal blocks: run this
document with just literate, then run scripts/literate-draw.py to
place xetal render's decorated form above every executable block.
The HTML literate exporter uses xetal render --html, so the pretty
X_eTaL is syntax-colored there. The APL blocks deliberately do not
execute; with APL mode/font-lock installed they are syntax-colored in
Emacs. A future small extension to literate-export.el can preserve
APL font-lock or render these blocks with an APL-specific highlighter
in HTML.
The examples below favor idioms that illuminate X_eTaL's vocabulary:
r_/, s_\, t_able, i_nner, trains, grade, selection, shape, and
whole-array match. They are therefore a language-design sample, not a
claim that every FinnAPL idiom should survive unchanged.
Setup
A few values are shared by later examples.
v ← 3 1 4 1 5 9 2 6 b ← 0 1 1 0 1 0 m ← 2 3 r̲eshape 1 2 3 4 5 6 ⍝ typed: ⍝ v := 3 1 4 1 5 9 2 6 ⍝ b := 0 1 1 0 1 0 ⍝ m := 2 3 r_eshape 1 2 3 4 5 6
Reduction: sum
FinnAPL #473 is simply reduction by addition.
+/X
X_eTaL makes the higher-order operation a word-like operator and quotes the function supplied to it.
'+ r̲/ v ⍝ typed: ⍝ '+ r_/ v
31
The extra spelling is intentional: r_/ is visibly the same abstraction
whether its operand is +, m_ax, a lambda, or a user function.
Reduction: maximum and minimum
FinnAPL #433 and #435:
⌈/X ⌊/X
'm̲ax r̲/ v 'm̲in r̲/ v ⍝ typed: ⍝ 'm_ax r_/ v ⍝ 'm_in r_/ v
9 1
This regularity matters more than preserving a separate glyph for every APL reduction.
Scan: cumulative sum
FinnAPL #410:
+\X
'+ s̲\ v ⍝ typed: ⍝ '+ s_\ v
3 4 8 9 14 23 25 31
s_\ is the running form of the same reduction vocabulary: every
prefix result is retained.
Grade and sorting
FinnAPL #47 sorts by grading and indexing:
X[⍋X]
X_eTaL has the direct operation:
s̲ort v ⍝ typed: ⍝ s_ort v
1 1 2 3 4 5 6 9
but retains grade as a first-class array idea:
g̲rade v (g̲rade v) s̲elect v ⍝ typed: ⍝ g_rade v ⍝ (g_rade v) s_elect v
2 4 7 1 3 5 8 6 1 1 2 3 4 5 6 9
The second expression is deliberately equivalent to s_ort v. Grade
is useful when the same permutation must order several related arrays.
Descending sort
FinnAPL #58:
X[⍒X]
X_eTaL can compose ascending sort with reverse:
r̲ev s̲ort v ⍝ typed: ⍝ r_ev s_ort v
9 6 5 4 3 2 1 1
This is a useful example of preferring composition over adding a second sorting primitive merely to mirror APL's grade-down glyph.
Boolean positions
FinnAPL has several historical forms for "indices of ones"; #594 is:
X/⍳⍴X
X_eTaL names the idea directly:
w̲here b ⍝ typed: ⍝ w_here b
2 3 5
This is shorter semantically even though it is longer typographically.
Unique items / nub
FinnAPL #205 removes duplicate elements while retaining first occurrence:
((X⍳X)=⍳⍴X)/X
u̲nique v ⍝ typed: ⍝ u_nique v
3 1 4 5 9 2 6
This is a good example of an old idiom becoming a library/builtin concept rather than remaining a puzzle to reconstruct at every use.
Multiplication table / outer product
FinnAPL #230:
(⍳X)∘.×⍳X
In X_eTaL, t_able names APL's outer-product idea.
(r̲ange 5) '× t̲able r̲ange 5 ⍝ typed: ⍝ (r_ange 5) '* t_able r_ange 5
1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
Read it as: make a table by applying multiplication to every left/right pair. The same operator accepts any dyadic function value.
Equality table / identity matrix
FinnAPL #286:
(⍳X)∘.=⍳X
(r̲ange 5) '= t̲able r̲ange 5 ⍝ typed: ⍝ (r_ange 5) '= t_able r_ange 5
1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1
The structure is identical to the multiplication table; only the function operand changes.
Dot product
FinnAPL #333:
Y+.×X
X_eTaL writes the reducing function and pairing function explicitly:
1 2 3 '+ '× i̲nner 4 5 6 ⍝ typed: ⍝ 1 2 3 '+ '* i_nner 4 5 6
32
The nearest operand, *, pairs items; + reduces them.
Matrix product
FinnAPL #336 uses exactly the same APL inner product:
X+.×Y
and X_eTaL deliberately generalizes the same expression from vectors to matrices.
m '+ '× i̲nner o̲\ m ⍝ typed: ⍝ m '+ '* i_nner o_\ m
14 32 32 77
This is one of the strongest correspondences between the two languages: the array abstraction survives, while the syntax makes the two roles of the inner product explicit.
Sum of squares
FinnAPL #334:
X+.×X
v '+ '× i̲nner v ⍝ typed: ⍝ v '+ '* i_nner v
173
For teaching, this version is interesting because it exposes "multiply corresponding items, then add" rather than introducing a special sum-of-squares operation.
Mean as a train
FinnAPL #471:
(+/X)÷⍴X
X_eTaL can express the same dataflow as a fork: sum and tally see the same argument, then their results are divided.
ᵘm̲ean ← ['+ r̲/ ÷ t̲ally] ᵘm̲ean v ⍝ typed: ⍝ u:m_ean := ['+ r_/ / t_ally] ⍝ u:m_ean v
3.875
This is a particularly useful X_eTaL comparison: square brackets make the train structure explicit rather than relying on tacit parsing alone.
Whole-array equality
Many FinnAPL idioms reduce elementwise equality. For equal-shaped arrays #458 is:
∧/,X=Y
X_eTaL distinguishes elementwise = from whole-array m_atch:
1 2 3 m̲atch 1 2 3 1 2 3 m̲atch 1 2 4 ⍝ typed: ⍝ 1 2 3 m_atch 1 2 3 ⍝ 1 2 3 m_atch 1 2 4
1 0
m_atch also checks shape, so it states the actual intention directly.
All and any
FinnAPL #461 and #444:
∧/X ∨/X
'∧ r̲/ b '∨ r̲/ b ⍝ typed: ⍝ '& r_/ b ⍝ '| r_/ b
0 1
Again, reduction is one concept with different function operands.
Even numbers
FinnAPL #551:
~2|X
X_eTaL uses the named remainder operation in mathematical argument order.
0 = v m̲od 2 ⍝ typed: ⍝ 0 = v m_od 2
0 0 1 0 0 0 1 1
To retain only the even values, combine the mask with w_here and
s_elect:
(w̲here 0 = v m̲od 2) s̲elect v ⍝ typed: ⍝ (w_here 0 = v m_od 2) s_elect v
4 2 6
Reverse and rotate
FinnAPL uses the same glyph family for reverse and rotate. For example, a one-place cyclic neighbor is written:
¯1⌽X
X_eTaL separates the named reverse operation from rotation:
r̲ev v -1 o̲- v 1 o̲- v ⍝ typed: ⍝ r_ev v ⍝ -1 o_- v ⍝ 1 o_- v
6 2 9 5 1 4 1 3 6 3 1 4 1 5 9 2 1 4 1 5 9 2 6 3
That distinction is slightly more verbose but easier to search, explain, and type-check.
Transpose
FinnAPL matrix transpose:
⍉X
X_eTaL's decorated form renders the ASCII o_\ operator:
o̲\ m ⍝ typed: ⍝ o_\ m
1 4 2 5 3 6
For higher-rank arrays X_eTaL also has t_ranspose for an explicit axis
permutation.
Range / consecutive integers
FinnAPL #658:
(X-⎕IO)+⍳1+Y-X
For the common 1-origin case, X_eTaL's structural vocabulary avoids an index-origin variable:
r̲ange 8 ⍝ typed: ⍝ r_ange 8
1 2 3 4 5 6 7 8
The historical idiom is also a reminder that X_eTaL should not reproduce
⎕IO-dependent programming merely for source similarity.
Number of elements and shape
FinnAPL #684 counts all elements by ravelling first:
⍴,X
t̲ally r̲avel m s̲hape m ⍝ typed: ⍝ t_ally r_avel m ⍝ s_hape m
6 2 3
Because X_eTaL distinguishes these structural questions by name, code can usually say which property it actually wants.
Zero array by scalar extension
FinnAPL #727:
0×X
The same array idea survives directly because X_eTaL scalar functions extend over arrays:
0 × m ⍝ typed: ⍝ 0 * m
0 0 0 0 0 0
No special zero-array constructor is necessary for this case.
Selection by a mask
FinnAPL #602 is compress:
Y/X
X_eTaL separates finding positions from selecting them:
(w̲here b) s̲elect 10 20 30 40 50 60 ⍝ typed: ⍝ (w_here b) s_elect 10 20 30 40 50 60
20 30 50
That is less glyph-dense, but both intermediate concepts are named and independently reusable.
What should not be translated literally
Some FinnAPL idioms are valuable precisely because X_eTaL should reject their technique. FinnAPL #133 constructs and executes an assignment to a dynamically generated variable name:
⍎'VAR',(⍕X),'←Y'
There should be no X_eTaL transliteration of this expression. Dynamic code generation fights static name resolution and static typing. The X_eTaL answer is normally to represent the varying association as data (an array, table, map-like library abstraction, or another typed structure), not to manufacture source code.
Likewise, old idioms based on →, ⍎, terminal state, or ⎕IO should
be classified before they are translated. A historical corpus is most
useful when it tells us which old techniques disappear, not only which
glyphs can be respelled.
Classification for the larger corpus
A full FinnAPL port should give every idiom one primary disposition:
| class | meaning |
|---|---|
| direct | the array idea maps naturally to existing X_eTaL |
| composed | ordinary X_eTaL composition is clearer than a new primitive |
| train | a bracketed train exposes the dataflow |
| library | a recurring domain idea deserves a named typed function |
| macro | syntax/control structure belongs in the extensibility layer |
| type-level | static typing makes a runtime test unnecessary or different |
| obsolete | the idiom solves a historical APL environment problem |
| reject | reproducing it would undermine X_eTaL's design |
| gap | the idiom exposes a useful missing X_eTaL capability |
The important metric is therefore not "how many APL one-liners can be transliterated?" It is "how many useful array ideas remain concise and compositional, and what do the failures teach us about the language?"
Reproducing the document
From the X_eTaL repository:
just literate scripts/literate-draw.py docs/literate/finnapl-idioms.org scripts/literate-html.sh
The first command executes the X_eTaL blocks and records their results.
The draw pass inserts the pretty Unicode rendering above each ASCII
source block. The HTML export invokes xetal render --html for the
X_eTaL blocks, preserving the language's syntax colors.
For APL, keep the source as #+begin_src apl rather than an example
block. That lets Emacs use APL font-lock when an APL major mode is
installed and leaves a clean hook for adding APL coloring to the custom
HTML exporter.