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.

Literate documents · Live demo · Repository