A literate tour of X_eTaL
every feature of the language, run as you read
Table of Contents
This is demos/tour.xtl as a literate program: the same code, in the
same order, with the reading between the blocks. Every block is live
X_eTaL, run by xetal through ob-xetal (docs/emacs/ob-xetal.el),
and its result is recorded under it. The blocks share one session,
so a definition made in an early block is still there in a later one,
as in the REPL.
The source is plain ASCII, as typed; xetal-mode shows :=, ->, _l
and _r as the glyphs the decorated form uses, and just tour prints
the whole tour decorated, each statement followed by its output.
To run it again: just literate (results are recorded in this file),
and just check-literate fails if a result has changed.
Numbers
X_eTaL numbers are Int or Float, and literals take whatever number type the context needs. An exponent written touching a value is a literal superscript (x^2 displays as x squared); a computed power is the spaced function ^. Division always gives a Float, the integer operations are named, and equality is exact, with a tolerant e_q~ beside it. There is no precedence: everything reads right to left, so the left operand of & needs its parentheses.
42 ⍝ an Int ⍝ typed: ⍝ 42 # an Int
42
-3 2.5 ⍝ a negative literal; a strand has one type (Float) ⍝ typed: ⍝ -3 2.5 # a negative literal; a strand has one type (Float)
-3.0 2.5
x ← 3 ⍝ ← binds; = is always equality x² ⍝ a literal exponent touches its value: superscript ⍝ typed: ⍝ x := 3 # `:=` binds; `=` is always equality ⍝ x^2 # a literal exponent touches its value: superscript
9
4⁻¹ ⍝ a negative exponent gives a Float (a literal base) ⍝ typed: ⍝ 4^-1 # a negative exponent gives a Float (a literal base)
0.25
2 ^ 10 ⍝ spaced ^ is the power function (computed exponents) ⍝ typed: ⍝ 2 ^ 10 # spaced `^` is the power function (computed exponents)
1024
7 ÷ 2 ⍝ ÷ always gives a Float ⍝ typed: ⍝ 7 / 2 # `/` always gives a Float
3.5
7 d̲iv 2◆ 7 m̲od 3 ⍝ integer quotient and remainder; ◆ separates statements ⍝ typed: ⍝ 7 d_iv 2; 7 m_od 3 # integer quotient and remainder; `;` separates statements
3 1
(0.1 + 0.2) = 0.3 ⍝ = is exact: 0 (false) ⍝ typed: ⍝ (0.1 + 0.2) = 0.3 # `=` is exact: 0 (false)
0
(0.1 + 0.2) e̲q~ 0.3 ⍝ tolerant equality: 1 ⍝ typed: ⍝ (0.1 + 0.2) e_q~ 0.3 # tolerant equality: 1
1
(3 < 4) ∧ 2 ≠ 2 ⍝ Bool (∧ ∨ ≠); no precedence: parenthesize the left ⍝ typed: ⍝ (3 < 4) & 2 != 2 # Bool (`& | !=`); no precedence: parenthesize the left
0
f̲loat 3 ⍝ Int to Float ⍝ typed: ⍝ f_loat 3 # Int to Float
3.0
Trigonometry works in radians; p_i @ is pi, a niladic function
called with Unit:
s̲in (p̲i @) ÷ 2 ⍝ trigonometry in radians; p̲i @ is pi (niladic) ⍝ typed: ⍝ s_in (p_i @) / 2 # trigonometry in radians; `p_i @` is pi (niladic)
1.0
a̲tan 1 ⍝ and c_os ⍝ typed: ⍝ a_tan 1 # and c_os
0.7853981633974483
Mutation
Values are immutable: := makes a new binding. The escape hatch is visible in the name: only a variable ending in ! may be updated in place.
count! ← 0 ⍝ only names ending in ! (like count!) may be reassigned count! ← count! + 1 count! ⍝ typed: ⍝ count! := 0 # only names ending in ! (like `count!`) may be reassigned ⍝ count! := count! + 1 ⍝ count!
1
Strings: vectors of characters
A string is a vector of characters, so every array function applies to it, and comparisons work item by item.
"hello" ⍝ typed: ⍝ "hello"
hello
"abc" = "abd" ⍝ item by item ⍝ typed: ⍝ "abc" = "abd" # item by item
1 1 0
3 t̲ake "hello" ⍝ typed: ⍝ 3 t_ake "hello"
hel
A string, and a comment, may hold any Unicode; the rest of the source stays ASCII:
"hello X̲ᵉTᵃL" ⍝ strings and comments may hold any Unicode (code may not) ⍝ typed: ⍝ "hello X̲ᵉTᵃL" # strings and comments may hold any Unicode (code may not)
hello X̲ᵉTᵃL
Arrays
Arrays are dense and 1-origin. A type names only the element type, so a scalar function such as * applies to a matrix unchanged and a scalar extends to every item. The count, shape or indices of a structural function go on its left.
m ← 2 3 r̲eshape r̲ange 6 ⍝ 1-origin: r̲ange 6 is 1 2 3 4 5 6 m ⍝ typed: ⍝ m := 2 3 r_eshape r_ange 6 # 1-origin: `r_ange 6` is 1 2 3 4 5 6 ⍝ m
1 2 3 4 5 6
s̲hape m◆ t̲ally m ⍝ typed: ⍝ s_hape m; t_ally m
2 3 2
m × 10 ⍝ a scalar extends to every item ⍝ typed: ⍝ m * 10 # a scalar extends to every item
10 20 30 40 50 60
2 s̲elect m ⍝ the 2nd major cell (row) ⍝ typed: ⍝ 2 s_elect m # the 2nd major cell (row)
4 5 6
-1 t̲ake m◆ 1 d̲rop m ⍝ take and drop count from the end when negative ⍝ typed: ⍝ -1 t_ake m; 1 d_rop m # take and drop count from the end when negative
4 5 6 4 5 6
(f̲irst m) c̲at 7 8 9 ⍝ join along the leading axis ⍝ typed: ⍝ (f_irst m) c_at 7 8 9 # join along the leading axis
1 2 3 7 8 9
r̲avel m ⍝ typed: ⍝ r_avel m
1 2 3 4 5 6
10 ^ r̲ev o̲ffsets 3 ⍝ o̲ffsets counts from 0: place values 100 10 1 ⍝ typed: ⍝ 10 ^ r_ev o_ffsets 3 # `o_ffsets` counts from 0: place values 100 10 1
100 10 1
Join along another axis with a subscript, replicate items, and encode and decode numbers in mixed radices:
m c̲at₂ 0 9 ⍝ ... or along axis 2: a column on the right ⍝ typed: ⍝ m c_at_2 0 9 # ... or along axis 2: a column on the right
1 2 3 0 4 5 6 9
1 0 2 r̲eplicate 7 8 9 ⍝ replicate: each item, as many times as its count ⍝ typed: ⍝ 1 0 2 r_eplicate 7 8 9 # replicate: each item, as many times as its count
7 9 9
10 10 10 e̲ncode 123 ⍝ encode: the digits, in the radices on the left ⍝ typed: ⍝ 10 10 10 e_ncode 123 # encode: the digits, in the radices on the left
1 2 3
24 60 60 d̲ecode 1 2 5 ⍝ decode: 1 hour 2 minutes 5 seconds, in seconds ⍝ typed: ⍝ 24 60 60 d_ecode 1 2 5 # decode: 1 hour 2 minutes 5 seconds, in seconds
3725
Functions
A function name has one underlined letter, typed as an underscore after it; the program's functions live in the u: namespace. _l and _r are a lambda's left and right arguments (drawn as alpha and omega), named parameters come before ->, guards return early, a niladic function takes @, and a ~ parameter is only evaluated when used.
ᵘs̲quare ← { ⍵ × ⍵ } ⍝ ⍵ is the right argument ᵘs̲quare 1 2 3 ⍝ scalar functions work on arrays unchanged ⍝ typed: ⍝ u:s_quare := { _r * _r } # `_r` is the right argument ⍝ u:s_quare 1 2 3 # scalar functions work on arrays unchanged
1 4 9
ᵘs̲ub ← { ⍺ − ⍵ } ⍝ ⍺ is the left argument 10 ᵘs̲ub 3 ⍝ dyadic use is currying: (ᵘs̲ub 10) 3 ⍝ typed: ⍝ u:s_ub := { _l - _r } # `_l` is the left argument ⍝ 10 u:s_ub 3 # dyadic use is currying: `(u:s_ub 10) 3`
7
ᵘh̲yp ← { a b → ((a × a) + b × b) ^ 0.5 } ⍝ named parameters before → 3 ᵘh̲yp 4 ⍝ typed: ⍝ u:h_yp := { a b -> ((a * a) + b * b) ^ 0.5 } # named parameters before `->` ⍝ 3 u:h_yp 4
5.0
ᵘs̲ign ← { x → x < 0 ? -1◆ x = 0 ? 0◆ 1 } ⍝ guards: condition ? result ᵘf̲act ← { n → n ≤ 1 ? 1 n × ᵘf̲act n − 1 } ⍝ typed: ⍝ u:s_ign := { x -> x < 0 ? -1; x = 0 ? 0; 1 } # guards: condition `?` result ⍝ u:f_act := { n -> ⍝ n <= 1 ? 1 ⍝ n * u:f_act n - 1 ⍝ }
ᵘf̲act 10 ⍝ recursion ⍝ typed: ⍝ u:f_act 10 # recursion
3628800
ᵘt̲wo ← { @ → 2 } ⍝ a niladic function takes @ ᵘt̲wo @ ⍝ typed: ⍝ u:t_wo := { @ -> 2 } # a niladic function takes `@` ⍝ u:t_wo @
2
ᵘk̲eep ← { a ~b → a } ⍝ ~b is a lazy parameter: evaluated 7 ᵘk̲eep 1 ÷ 0 ⍝ only if used, so no division by zero ⍝ typed: ⍝ u:k_eep := { a ~b -> a } # `~b` is a lazy parameter: evaluated ⍝ 7 u:k_eep 1 / 0 # only if used, so no division by zero
7
(ᵘs̲ub 100)_ 1 ⍝ (expr)_ applies a function value ⍝ typed: ⍝ (u:s_ub 100)_ 1 # `(expr)_` applies a function value
99
ᵘt̲wice ← { f̲ x → f̲ f̲ x } ⍝ apply a function parameter two times '{ ⍵ + 10 } ᵘt̲wice 3 ⍝ 3 + 10 + 10 ⍝ typed: ⍝ u:t_wice := { f_ x -> f_ f_ x } # apply a function parameter two times ⍝ '{ _r + 10 } u:t_wice 3 # 3 + 10 + 10
23
'ᵘs̲quare ᵘt̲wice 3 ⍝ square (square 3) = 9 squared ⍝ typed: ⍝ 'u:s_quare u:t_wice 3 # square (square 3) = 9 squared
81
Quotes and operands
A quote passes a function as a value. Written just left of a function name it becomes that function's operand, which gives the APL look: '+ r_/ is plus-reduce. Reduce folds from the right along the leading axis; scan gives the prefix reductions; each, table and inner are ordinary curried functions.
'+ r̲/ 1 2 3 4 ⍝ a quoted function is the operand of r̲/ (reduce) ⍝ typed: ⍝ '+ r_/ 1 2 3 4 # a quoted function is the operand of `r_/` (reduce)
10
'− r̲/ 1 2 3 ⍝ reduce folds from the right: 1 - (2 - 3) ⍝ typed: ⍝ '- r_/ 1 2 3 # reduce folds from the right: 1 - (2 - 3)
2
'+ s̲\ 1 2 3 4 ⍝ scan: the prefix reductions ⍝ typed: ⍝ '+ s_\ 1 2 3 4 # scan: the prefix reductions
1 3 6 10
'+ r̲/ m ⍝ the leading axis: column sums ⍝ typed: ⍝ '+ r_/ m # the leading axis: column sums
5 7 9
'ᵘs̲ign e̲ach -5 0 5 ⍝ each: apply to every item ⍝ typed: ⍝ 'u:s_ign e_ach -5 0 5 # each: apply to every item
-1 0 1
1 2 3 '= e̲ach 1 5 3 ⍝ dyadic each is currying ⍝ typed: ⍝ 1 2 3 '= e_ach 1 5 3 # dyadic each is currying
1 0 1
1 2 3 '× t̲able 1 2 3 ⍝ table: the outer product ⍝ typed: ⍝ 1 2 3 '* t_able 1 2 3 # table: the outer product
1 2 3 2 4 6 3 6 9
m '+ '× i̲nner 1 1 1 ⍝ inner product: the nearest operand pairs ⍝ typed: ⍝ m '+ '* i_nner 1 1 1 # inner product: the nearest operand pairs
6 15
'n̲eg 'a̲bs c̲ompose -4 ⍝ compose: the nearest operand applies first ⍝ typed: ⍝ 'n_eg 'a_bs c_ompose -4 # compose: the nearest operand applies first
-4
2 '÷ s̲wap 1 ⍝ swap the arguments: 1 / 2 ⍝ typed: ⍝ 2 '/ s_wap 1 # swap the arguments: 1 / 2
0.5
'{ ⍺ + ⍵ } r̲/ 1 2 3 ⍝ a quoted lambda is an operand too ⍝ typed: ⍝ '{ _l + _r } r_/ 1 2 3 # a quoted lambda is an operand too
6
Trains and tacks
A train in brackets composes functions without naming the argument: a fork applies the outer two and combines them with the middle one; two functions are an atop. The tacks are `x l_eft y`, which is x, and `x r_ight y`, which is y. Each train is the lambda it stands for, written without naming the argument; the comments spell each one out.
ᵘa̲vg ← ['+ r̲/ ÷ t̲ally] ⍝ fork: ('+ r̲/ x) ÷ t̲ally x ᵘa̲vg 1 2 3 4 { x → ('+ r̲/ x) ÷ t̲ally x } 1 2 3 4 ⍝ the same, spelled out ⍝ typed: ⍝ u:a_vg := ['+ r_/ / t_ally] # fork: `('+ r_/ x) / t_ally x` ⍝ u:a_vg 1 2 3 4 ⍝ { x -> ('+ r_/ x) / t_ally x } 1 2 3 4 # the same, spelled out
2.5 2.5
[n̲eg a̲bs] -5 ⍝ atop: n̲eg a̲bs x ⍝ typed: ⍝ [n_eg a_bs] -5 # atop: `n_eg a_bs x`
-5
3 [l̲eft + r̲ight] 4 ⍝ dyadic fork: (x l̲eft y) + (x r̲ight y), so x + y ⍝ typed: ⍝ 3 [l_eft + r_ight] 4 # dyadic fork: `(x l_eft y) + (x r_ight y)`, so `x + y`
7
[i̲d − n̲eg] 5 ⍝ hook: x − n̲eg x ⍝ typed: ⍝ [i_d - n_eg] 5 # hook: `x - n_eg x`
10
A train between two values is dyadic, and a longer train groups from the right into forks: the one below is `[f_irst c_at [… c_at …]]`. A quoted train is an operand like any function.
1 2 [+ × −] 3 4 ⍝ dyadic fork: (x + y) × (x − y) ⍝ typed: ⍝ 1 2 [+ * -] 3 4 # dyadic fork: `(x + y) * (x - y)`
-8 -12
[f̲irst c̲at 'm̲ax r̲/ c̲at 'm̲in r̲/] 3 1 4 1 5 ⍝ (f̲irst x) c̲at ('m̲ax r̲/ x) c̲at 'm̲in r̲/ x ⍝ typed: ⍝ [f_irst c_at 'm_ax r_/ c_at 'm_in r_/] 3 1 4 1 5 # `(f_irst x) c_at ('m_ax r_/ x) c_at 'm_in r_/ x`
3 5 1
'[t̲ally d̲isclose] e̲ach "ab" "cde" "f" ⍝ each item i: t̲ally d̲isclose i ⍝ typed: ⍝ '[t_ally d_isclose] e_ach "ab" "cde" "f" # each item i: `t_ally d_isclose i`
2 3 1
Rotate, reverse and axes
Rotate and reverse work on the leading axis; a subscript names another, and any function takes one by the same move-to-front rule. A list of amounts gives every rotation, which is exactly what Life's neighbor count needs.
1 o̲- 1 2 3 4 ⍝ rotate toward the front ⍝ typed: ⍝ 1 o_- 1 2 3 4 # rotate toward the front
2 3 4 1
r̲ev "stressed" ⍝ typed: ⍝ r_ev "stressed"
desserts
⍝ A function works on the leading axis (axis 1) unless a subscript names ⍝ typed:
⍝ another: r̲ev₂ m moves axis 2 to the front, applies r̲ev, and ⍝ typed:
⍝ moves it back. '+ r̲/ m ⍝ implicit: axis 1, so column sums ⍝ typed: ⍝ '+ r_/ m # implicit: axis 1, so column sums
5 7 9
'+ r̲/₁ m ⍝ the same, explicit ⍝ typed: ⍝ '+ r_/_1 m # the same, explicit
5 7 9
'+ r̲/₂ m ⍝ axis 2: row sums ⍝ typed: ⍝ '+ r_/_2 m # axis 2: row sums
6 15
'+ s̲\₂ m ⍝ running sums along each row ⍝ typed: ⍝ '+ s_\_2 m # running sums along each row
1 3 6 4 9 15
1 o̲- m ⍝ rotate the rows (axis 1) ⍝ typed: ⍝ 1 o_- m # rotate the rows (axis 1)
4 5 6 1 2 3
1 o̲-₁ m ⍝ the same, explicit ⍝ typed: ⍝ 1 o_-_1 m # the same, explicit
4 5 6 1 2 3
1 o̲-₂ m ⍝ rotate within each row (axis 2) ⍝ typed: ⍝ 1 o_-_2 m # rotate within each row (axis 2)
2 3 1 5 6 4
r̲ev₂ m ⍝ reverse each row ⍝ typed: ⍝ r_ev_2 m # reverse each row
3 2 1 6 5 4
'+ r̲/₁₂ m ⍝ two axes in turn: the total ⍝ typed: ⍝ '+ r_/_12 m # two axes in turn: the total
21
-1 0 1 o̲- 1 2 3 ⍝ a list of amounts gives every rotation ⍝ typed: ⍝ -1 0 1 o_- 1 2 3 # a list of amounts gives every rotation
3 1 2 1 2 3 2 3 1
s̲hape -1 0 1 o̲-₁₂ m ⍝ every combination along both axes: 3 3 2 3 ⍝ typed: ⍝ s_hape -1 0 1 o_-_12 m # every combination along both axes: 3 3 2 3
3 3 2 3
Transpose reverses the order of the axes, so a matrix's rows become
its columns. A subscript of two axes swaps just those, and
t_ranspose moves every axis to the place its list names.
o̲\ m ⍝ transpose: rows become columns ⍝ typed: ⍝ o_\ m # transpose: rows become columns
1 4 2 5 3 6
a ← 2 3 4 r̲eshape r̲ange 24 s̲hape o̲\ a ⍝ every axis reversed: 4 3 2 s̲hape o̲\₂₃ a ⍝ axes 2 and 3 swapped: 2 4 3 s̲hape 3 1 2 t̲ranspose a ⍝ axis 1 to 3, 2 to 1, 3 to 2: 3 4 2 ⍝ typed: ⍝ a := 2 3 4 r_eshape r_ange 24 ⍝ s_hape o_\ a # every axis reversed: 4 3 2 ⍝ s_hape o_\_23 a # axes 2 and 3 swapped: 2 4 3 ⍝ s_hape 3 1 2 t_ranspose a # axis 1 to 3, 2 to 1, 3 to 2: 3 4 2
4 3 2 2 4 3 3 4 2
Search and order
The search and order built-ins treat major cells as items: sort is stable, grade gives the indices that sort, index-of answers tally + 1 for what is absent.
v ← 3 1 4 1 5 9 2 6 s̲ort v◆ g̲rade v ⍝ sort, and the indices that sort ⍝ typed: ⍝ v := 3 1 4 1 5 9 2 6 ⍝ s_ort v; g_rade v # sort, and the indices that sort
1 1 2 3 4 5 6 9 2 4 7 1 3 5 8 6
u̲nique v ⍝ typed: ⍝ u_nique v
3 1 4 5 9 2 6
v i̲ndexOf 5 7 ⍝ 7 is absent: tally + 1 ⍝ typed: ⍝ v i_ndexOf 5 7 # 7 is absent: tally + 1
5 9
2 7 m̲ember? v ⍝ typed: ⍝ 2 7 m_ember? v
1 0
w̲here v > 4 ⍝ indices of the 1s ⍝ typed: ⍝ w_here v > 4 # indices of the 1s
5 6 8
Nested arrays and display
An item can itself be an array, in a box: a strand of strings is a vector of boxes, and nested values print framed, as APL2's DISPLAY draws them.
n ← "ab" "cde" ⍝ a strand of strings: a vector of 2 boxes n ⍝ nested values print framed (APL2's DISPLAY) ⍝ typed: ⍝ n := "ab" "cde" # a strand of strings: a vector of 2 boxes ⍝ n # nested values print framed (APL2's DISPLAY)
┌→───────────┐ │ ┌→─┐ ┌→──┐ │ │ │ab│ │cde│ │ │ └──┘ └───┘ │ └∊───────────┘
t̲ally n ⍝ typed: ⍝ t_ally n
2
d̲isclose 2 s̲elect n ⍝ open the 2nd box ⍝ typed: ⍝ d_isclose 2 s_elect n # open the 2nd box
cde
Partition cuts a vector into boxed pieces where its mask is 0; map applies a function to each item and boxes each result, so a result may be an array:
s ← "to be or not" (s ≠ f̲irst " ") p̲artition s ⍝ cut where the mask is 0: the words, boxed ⍝ typed: ⍝ s := "to be or not" ⍝ (s != f_irst " ") p_artition s # cut where the mask is 0: the words, boxed
┌→─────────────────────┐ │ ┌→─┐ ┌→─┐ ┌→─┐ ┌→──┐ │ │ │to│ │be│ │or│ │not│ │ │ └──┘ └──┘ └──┘ └───┘ │ └∊─────────────────────┘
'r̲ange m̲ap 1 2 3 ⍝ map: each result boxed, so it may be an array ⍝ typed: ⍝ 'r_ange m_ap 1 2 3 # map: each result boxed, so it may be an array
┌→──────────────────┐ │ ┌→┐ ┌→──┐ ┌→────┐ │ │ │1│ │1 2│ │1 2 3│ │ │ └~┘ └~──┘ └~────┘ │ └∊──────────────────┘
d_isplay frames any value, flat ones too, as a character matrix;
xetal --box prints every array result that way:
d̲isplay m ⍝ any value framed, as a character matrix (xetal --box prints all so) ⍝ typed: ⍝ d_isplay m # any value framed, as a character matrix (xetal --box prints all so)
┌→────┐ ↓1 2 3│ │4 5 6│ └~────┘
Effects
Effects end in !. The rolls are random; this document fixes them with :seed so its results stay the same each time it is run.
r̲oll! 6 6 6 ⍝ three dice: random 1..6 each, so every run differs ⍝ typed: ⍝ r_oll! 6 6 6 # three dice: random 1..6 each, so every run differs
6 2 1
r̲oll! 6 6 6 ⍝ (and each line rolls again) ⍝ typed: ⍝ r_oll! 6 6 6 # (and each line rolls again)
6 4 3
r̲oll! 6 6 6 ⍝ typed: ⍝ r_oll! 6 6 6
4 4 1
p̲rint! "printed, then returned" ⍝ p̲rint! prints and returns its argument ⍝ typed: ⍝ p_rint! "printed, then returned" # `p_rint!` prints and returns its argument
printed, then returned printed, then returned
Pictures
[]G_RID draws an array as a grid of cells and []P_ATH points as a
line, each giving the picture as SVG text; []S_HOW shows it (a
numbered file on the command line, which just draw FILE opens; in the
live demo, under the output). Here only the start of the text:
15 t̲ake ⎕G̲RID 2 2 r̲eshape 1 0 0 1 ⍝ typed: ⍝ 15 t_ake []G_RID 2 2 r_eshape 1 0 0 1
<svg xmlns="htt
Libraries
A library is an ordinary X_eTaL file of definitions: its exported
names start with l:, the rest are private. A program imports it with
u_se< under an alias of its choosing, and uses the exports through
that alias. Stats is a standard library, built into xetal.
ˢ⁼u̲se< "Stats" ⍝ import a library under an alias of your choosing ˢm̲ean 2 4 4 4 5 5 7 9 ⍝ its exported names, used through the alias ⍝ typed: ⍝ "s:" u_se< "Stats" # import a library under an alias of your choosing ⍝ s:m_ean 2 4 4 4 5 5 7 9 # its exported names, used through the alias
5.0
ˢs̲d 2 4 4 4 5 5 7 9 ⍝ the standard deviation ⍝ typed: ⍝ s:s_d 2 4 4 4 5 5 7 9 # the standard deviation
2.0
ˢr̲ange 3 1 4 1 5 ⍝ largest minus smallest ⍝ typed: ⍝ s:r_ange 3 1 4 1 5 # largest minus smallest
4
A library of your own lives in userlibs/, on the search path:
ʰ⁼u̲se< "Hello" ⍝ a library of your own, found in userlibs/ ʰh̲ello @ ⍝ niladic: called with Unit ⍝ typed: ⍝ "h:" u_se< "Hello" # a library of your own, found in userlibs/ ⍝ h:h_ello @ # niladic: called with Unit
hello X̲ᵉTᵃL
Combinators and power
Combinators is a second standard library: the birds of Raymond
Smullyan's To Mock a Mockingbird, functions that only rearrange,
repeat or drop their arguments (docs/birds.md lists them all and
decodes a few). A quoted function before a bird is its first
argument, the nearest first; a value on the left comes next, then the
one on the right.
ᶜ⁼u̲se< "Combinators" ⍝ Smullyan's birds, a standard library 1 ᶜK̲ 2 ⍝ K keeps its first argument ⍝ typed: ⍝ "c:" u_se< "Combinators" # Smullyan's birds, a standard library ⍝ 1 c:K_ 2 # K keeps its first argument
1
10 '− ᶜC̲ 3 ⍝ C swaps the arguments: 3 - 10 ⍝ typed: ⍝ 10 '- c:C_ 3 # C swaps the arguments: 3 - 10
-7
'n̲eg 'a̲bs ᶜB̲ -5 ⍝ B composes, the nearest operand last: a_bs n_eg -5 ⍝ typed: ⍝ 'n_eg 'a_bs c:B_ -5 # B composes, the nearest operand last: a_bs n_eg -5
5
'× ᶜW̲ 4 ⍝ W uses its argument twice: 4 * 4 ⍝ typed: ⍝ '* c:W_ 4 # W uses its argument twice: 4 * 4
16
Y makes recursion from a function that is handed itself; its self parameter is lazy (~), so Y unfolds one step at a time:
ᵘt̲riangle ← { ~s̲elf n → n ≤ 1 ? 1◆ n + s̲elf n − 1 } 'ᵘt̲riangle ᶜY̲ 5 ⍝ Y: recursion, from a function handed itself: 1+2+3+4+5 ⍝ typed: ⍝ u:t_riangle := { ~s_elf n -> n <= 1 ? 1; n + s_elf n - 1 } ⍝ 'u:t_riangle c:Y_ 5 # Y: recursion, from a function handed itself: 1+2+3+4+5
15
A superscript on a function name repeats the function (function
power); n 'f_ p_ower x does the same with a computed count:
n̲eg³ 5 ⍝ a superscript repeats a function: n_eg three times ⍝ typed: ⍝ n_eg^3 5 # a superscript repeats a function: n_eg three times
-5
Life in one line
Everything above comes together in Conway's Life: rotate the board by every offset in -1 0 1 along both axes, sum the nine boards to count each cell and its neighbors, and apply the rule. This is the language's acceptance test.
ᵘl̲ife ← { ('+ r̲/₁₂ -1 0 1 o̲-₁₂ ⍵) { (⍺ = 3) + ⍵ × ⍺ = 4 } ⍵ } ᵘl̲ife 5 5 r̲eshape 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 ⍝ typed: ⍝ u:l_ife := { ('+ r_/_12 -1 0 1 o_-_12 _r) { (_l = 3) + _r * _l = 4 } _r } ⍝ u:l_ife 5 5 r_eshape 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0
Not shown here
The textbook Y combinator, built from self-application, needs
--untyped (see demos/fixed-point.xtl and demos/birds-untyped.xtl);
docs/literate/birds.org runs the whole Combinators library.