Output & Basics
Display a number
puts 42 42 In APL, the value of the last expression in a program is displayed automatically — there is no
puts or print. Typing a literal or expression at the top level causes it to be shown. This is the model for all examples in this cheatsheet.Display a string
puts "hello" 'hello' APL strings are called character vectors — they are literally arrays of single characters. Single quotes delimit them; there is no double-quote syntax. The string
'hello' is a 5-element array where each element is a character.Result of arithmetic
puts 6 * 7 6×7 APL uses
× (U+00D7) for multiplication rather than *. The * symbol means "power" in APL (2*10 = 1024). Arithmetic results are displayed automatically as the program's return value.Comments
# This is a comment
puts 42 ⍝ This is a comment
42 APL uses
⍝ (the "lamp" symbol, U+235D) for line comments. Everything from ⍝ to the end of the line is ignored. There are no block comments.Assign and display
greeting = "Hello, World!"
puts greeting greeting←'Hello, World!'
greeting APL uses
← (left arrow, U+2190) for assignment. Unlike Ruby's =, assignment in APL is not an expression — it does not return the value for display. The second line greeting explicitly names the variable to display it.Arrays & Vectors
Numeric vector literal
numbers = [1, 2, 3, 4, 5]
p numbers 1 2 3 4 5 In APL, space-separated numbers form a vector (one-dimensional array). There are no brackets or commas — spaces are the separator. This is the fundamental data structure in APL; even a single number is treated as a scalar that can participate in array operations.
Integer range (⍳)
(0...5).to_a
# => [0, 1, 2, 3, 4] ⍳5 The
⍳ (iota) function generates a sequence from 0 to n−1. In ngn/apl, the index origin is always 0, so ⍳5 gives 0 1 2 3 4. The traditional Dyalog APL default is 1-based (⍳5 = 1 2 3 4 5), but this is configurable there via ⎕IO. The 0-based behavior matches Ruby's (0...5).to_a.Count elements (≢)
[1, 2, 3, 4, 5].length ≢1 2 3 4 5 The
≢ (tally) function counts the number of major cells in an array. For a vector, this is the number of elements. It is equivalent to Ruby's .length or .size. A related function ⍴ (shape) returns the shape of the array as a vector of dimensions.Nested array
nested = [[1, 2], [3, 4]]
p nested (1 2)(3 4) Parentheses create enclosed sub-arrays, resulting in a nested array. Each element of the outer vector is itself a vector. APL distinguishes between simple arrays (where every element is a scalar) and nested arrays (where elements can themselves be arrays). Nested arrays are a Dyalog APL extension; traditional APL only had simple rectangular arrays.
Create a matrix
matrix = [[1, 2, 3], [4, 5, 6]]
p matrix 2 3⍴1 2 3 4 5 6 The dyadic form of
⍴ (reshape) takes a shape vector on the left and data on the right, producing a multidimensional array. 2 3⍴... creates a 2-row, 3-column matrix. The data fills in row-major order. If the data is shorter than needed, it wraps around.Empty vector
[].length ≢⍳0 ⍳0 generates an empty integer vector (a vector of shape 0). Taking the tally of it confirms it has 0 elements. An empty vector in APL is a typed entity — it remembers it is a numeric (or character) array even though it contains nothing.Arithmetic
Right-to-left evaluation
# Ruby uses standard operator precedence
2 * 3 + 4 # => 10 (multiply first) 2×3+4 APL has no operator precedence. All expressions evaluate strictly right to left:
2×3+4 computes 3+4 first (= 7), then 2×7 (= 14). In Ruby, 2 * 3 + 4 evaluates to 10 because * has higher precedence than +. Use parentheses to override APL's right-to-left order.Element-wise array arithmetic
[1, 2, 3].zip([4, 5, 6]).map { |a, b| a + b } 1 2 3+4 5 6 In APL, arithmetic operators work element-by-element on arrays of the same shape — no
zip or map needed. When two arrays of equal length are added, the result is a vector of pairwise sums. This "array thinking" replaces the need for most explicit loops.Scalar broadcasts across array
[1, 2, 3, 4, 5].map { |x| x * 2 } 2×1 2 3 4 5 When one argument is a scalar and the other is an array, APL automatically broadcasts the scalar to match the array's shape. Every element of the array is multiplied by 2. This is the most common pattern for applying a constant operation to every element.
Division always returns exact result
7 / 2 # => 3 (integer division!)
7.0 / 2 # => 3.5 7÷2 APL's
÷ operator always returns exact mathematical results — 7÷2 gives 3.5, not 3. There is no distinction between integer and float division. This contrasts with Ruby, where 7 / 2 performs integer division and returns 3 because both operands are integers.Negative numbers (high minus ¯)
negative = -3
puts negative ¯3 APL uses
¯ (high minus, U+00AF) to denote negative literal numbers, separate from the - subtraction operator. This distinction allows APL to parse 3 ¯1 4 as the three-element vector 3 -1 4 unambiguously. Writing -3 in APL means "negate 3" (a monadic minus applied to 3) rather than "the literal negative three".Power / exponentiation
2 ** 10 2*10 APL uses
* for exponentiation, not multiplication. This is the opposite of Ruby (and most other languages), where ** is power and * is multiply. APL uses × (the multiplication sign character) for multiplication.Floor and ceiling
3.7.floor # => 3
3.2.ceil # => 4 ⌊3.7
⌈3.2 APL uses
⌊ (floor) and ⌈ (ceiling) as monadic functions. These are the same operations as Ruby's .floor and .ceil, but written as prefix operators rather than method calls. Both also work as dyadic functions: 3⌊5 returns the minimum of 3 and 5.Strings
String (character vector)
greeting = "hello"
puts greeting greeting←'hello'
greeting APL strings are character vectors — arrays where every element is a single character. This means all array operations apply to strings: you can reverse them with
⌽, take the first three characters with 3↑, or check membership with ∊. Single quotes are the only string delimiter; there are no double-quote strings.String length
"hello".length ≢'hello' Since strings are arrays,
≢ (tally) gives the length. The tally function counts major cells — for a character vector, that is the number of characters. There is no special string-length method because strings are not a distinct type from arrays.String concatenation
"hello" + " " + "world" 'hello',' ','world' The comma
, operator concatenates (catenates) arrays in APL, including character vectors. It works for any arrays, not just strings, and concatenates along the first axis. This is one of APL's most-used operators since combining arrays is fundamental to array programming.Reverse a string
"hello".reverse ⌽'hello' The
⌽ (rotate/reverse) function reverses a vector when used monadically. Because strings are arrays, the same operator that reverses a numeric vector also reverses a string — there is no need for a separate string-reverse method.Character membership / include?
"hello".include?("e") 'e'∊'hello' The
∊ (epsilon, "element of") operator tests membership. When the left argument is a single character and the right is a string, it returns 1 if the character appears in the string. The result is a Boolean integer: 1 for true, 0 for false. When the left argument has multiple elements, ∊ returns a Boolean vector of the same length.Character at index
"hello"[1] # => "e" (0-based) 'hello'[1] Bracket indexing on a character vector retrieves the character at the given position. In ngn/apl, indexing is 0-based, so index 1 gives the second character
'e'. In traditional Dyalog APL with default ⎕IO←1, index 1 would give 'h'.Array Shape & Reshaping
Shape of a vector (⍴)
numbers = [1, 2, 3, 4, 5]
puts numbers.length numbers←1 2 3 4 5
⍴numbers The monadic
⍴ (rho/shape) function returns the shape of an array as a vector of dimension sizes. For a vector of 5 elements, ⍴ returns the one-element vector 5. For a 2×3 matrix it returns 2 3. The "rank" (number of dimensions) is the length of the shape vector: ≢⍴array.Reshape an array (dyadic ⍴)
[1, 2, 3, 4, 5, 6].each_slice(3).to_a 2 3⍴1 2 3 4 5 6 The dyadic form of
⍴ creates a new array with the shape given on the left, filled with data from the right. The data fills row by row. If the data has fewer elements than needed, it wraps around (cyclic reuse). This operator is fundamental — it converts a flat sequence into a structured array of any shape.Shape of a matrix
matrix = [[1, 2, 3], [4, 5, 6]]
puts [matrix.length, matrix[0].length].inspect matrix←2 3⍴1 2 3 4 5 6
⍴matrix The shape of a matrix is a 2-element vector giving the number of rows and columns.
⍴matrix returns 2 3 for a 2-row, 3-column matrix. The rank of the matrix (number of dimensions) is ≢⍴matrix, which gives 2.Transpose a matrix
[[1, 2, 3], [4, 5, 6]].transpose ⍉2 3⍴1 2 3 4 5 6 The monadic
⍉ (transpose) function exchanges the axes of an array. A 2×3 matrix becomes a 3×2 matrix. For higher-rank arrays, ⍉ reverses the axis order. The result has the same elements but rearranged so that rows become columns.Take and drop
numbers = [1, 2, 3, 4, 5]
puts numbers.first(3).inspect
puts numbers.drop(3).inspect numbers←1 2 3 4 5
3↑numbers
3↓numbers The
↑ (take) function selects the first n elements; ↓ (drop) removes the first n elements. Both work on any axis of an array. Negative arguments take or drop from the end: ¯2↑numbers returns the last two elements. This is more general than Ruby's .first and .drop.Flatten a nested array
[[1, 2], [3, 4]].flatten ∊(1 2)(3 4) The monadic
∊ (enlist) function flattens a nested array into a simple vector, collecting all scalar elements regardless of nesting depth. For a simple (non-nested) array, ∊ returns the array unchanged. This is equivalent to Ruby's .flatten but works uniformly on arrays of any rank.Selection & Indexing
Index a single element
numbers = [10, 20, 30, 40, 50]
numbers[2] # => 30 numbers←10 20 30 40 50
numbers[2] Bracket indexing in ngn/apl uses 0-based indices, matching Ruby.
numbers[2] selects the element at position 2, which is 30. In traditional Dyalog APL with ⎕IO←1, the same bracket expression would select the second element (20). The 0-based convention in ngn/apl makes the Ruby comparison straightforward.Select multiple elements by index
numbers = [10, 20, 30, 40, 50]
numbers.values_at(0, 2, 4) numbers←10 20 30 40 50
numbers[0 2 4] APL bracket indexing accepts a vector of indices, returning a vector of the selected elements. This is sometimes called "fancy indexing." Unlike Ruby's
.values_at, this works for any shape of index array and can also be used to select rows or columns of a matrix.Filter with a Boolean mask
numbers = [1, 2, 3, 4, 5]
numbers.select { |x| x > 3 } numbers←1 2 3 4 5
(numbers>3)/numbers The dyadic
/ (compress/replicate) operator selects elements where the Boolean mask on the left is 1. The expression numbers>3 produces the Boolean vector 0 0 0 1 1, and then / keeps only the corresponding elements of numbers. This replaces .select and is a core APL idiom.Select by explicit Boolean mask
[1, 2, 3, 4].zip([true, false, true, false])
.select { |_, keep| keep }.map(&:first) 1 0 1 0/1 2 3 4 A literal Boolean vector on the left of
/ selects elements directly. 1 0 1 0/1 2 3 4 keeps elements at positions where the mask is 1, giving 1 3. This pattern is ubiquitous in APL: generate a Boolean vector from a condition, then compress with it. There is no equivalent single-operator idiom in Ruby.Index into a matrix
matrix = [[1, 2, 3], [4, 5, 6]]
matrix[1][2] # => 6 matrix←2 3⍴1 2 3 4 5 6
matrix[1;2] Matrix elements are indexed with a semicolon separating the row and column indices inside brackets:
matrix[row;col]. The row and column indices are 0-based in ngn/apl. Omitting an index selects the entire dimension: matrix[0;] selects the entire first row.Boolean & Logic
Boolean values are 0 and 1
puts 3 > 2 # => true
puts 3 < 2 # => false 3>2
3<2 APL has no
true or false keywords. Comparison operators produce integers: 1 for true and 0 for false. This means Boolean arrays can be used directly in arithmetic — counting true values is just +/boolean_vector (sum the 1s). This unification of Booleans and integers is a defining characteristic of APL.Comparison produces a Boolean vector
[1, 2, 3, 4, 5].map { |x| x > 3 } 1 2 3 4 5>3 Because comparison operators work on arrays, comparing a vector to a scalar produces a Boolean vector of the same length. Each element is compared independently. The result
0 0 0 1 1 can then be used as a mask for compression, counted with +/, or combined with other Boolean vectors.AND, OR, NOT
puts true && false # => false
puts true || false # => true
puts !true # => false 1∧0
1∨0
~1 APL uses
∧ for logical AND, ∨ for logical OR, and ~ for logical NOT. Like all APL operators, these work element-by-element on arrays. 1∧0 gives 0; 1∨0 gives 1; ~1 gives 0. On Boolean vectors: 1 0 1∧1 1 0 gives 1 0 0.NOT applied to a vector
[1, 0, 1, 0].map { |x| x == 0 ? 1 : 0 } ~1 0 1 0 The monadic
~ (not) function applied to a Boolean vector flips every element: 1 becomes 0, 0 becomes 1. This is equivalent to 1-vector for Boolean vectors. The clean, element-wise behavior means there is rarely a need for an explicit loop.All-true and any-true
[1, 1, 1].all? { |x| x == 1 } # true
[0, 1, 0].any? { |x| x == 1 } # true ∧/1 1 1
∨/0 1 0 The AND-reduction
∧/ returns 1 only if all elements are 1 — equivalent to Ruby's .all?. The OR-reduction ∨/ returns 1 if any element is 1 — equivalent to .any?. These are specific instances of the general reduce pattern where any function can be folded over an array with /.Sorting & Searching
Grade up — sort indices (⍋)
numbers = [3, 1, 4, 1, 5]
numbers.each_with_index.sort_by { |x, _| x }.map(&:last) ⍋3 1 4 1 5 The
⍋ (grade up) function does not sort the array — it returns the indices that would sort it in ascending order. For 3 1 4 1 5, grade up gives 1 3 0 2 4: the smallest element (1) is at position 1, the next smallest (1) at position 3, and so on. This is a building block for sorting and rank operations.Sort ascending
[3, 1, 4, 1, 5].sort numbers←3 1 4 1 5
numbers[⍋numbers] To sort a vector, index it by its own grade-up:
numbers[⍋numbers]. The grade-up gives the sorted order of indices, and indexing selects elements in that order. This two-step pattern (grade then index) is APL's canonical sort. It also allows sorting one array by the order of another.Sort descending
[3, 1, 4, 1, 5].sort.reverse numbers←3 1 4 1 5
numbers[⍒numbers] The
⍒ (grade down) function returns indices that would sort the array in descending order. Indexing by these gives the sorted-descending array. Grade up and grade down are complementary: array[⍒array] is equivalent to array[⌽⍋array] (reverse of grade up).Membership test (∊)
haystack = [1, 2, 3, 4, 5]
[3, 6, 1].map { |x| haystack.include?(x) } haystack←1 2 3 4 5
3 6 1∊haystack The dyadic
∊ (epsilon) operator tests whether each element of the left array appears anywhere in the right array, returning a Boolean vector. 3 6 1∊1 2 3 4 5 returns 1 0 1: 3 and 1 are present, 6 is not. The result always has the same shape as the left argument.Find index of element (dyadic ⍳)
haystack = [10, 20, 30, 40, 50]
haystack.index(30) haystack←10 20 30 40 50
haystack⍳30 The dyadic
⍳ (index of) function searches the left array for each element of the right argument and returns the position of its first occurrence. In ngn/apl, positions are 0-based. If an element is not found, the result is the length of the haystack (one past the last valid index) — a sentinel value rather than nil.Reduction & Scanning
Sum (reduce with +)
[1, 2, 3, 4, 5].sum +/1 2 3 4 5 The
/ operator (reduce, also called fold) inserts a function between each pair of elements. +/1 2 3 4 5 computes 1+2+3+4+5 = 15. Any APL function can be used with /: ×/ for product, ⌈/ for maximum, ⌊/ for minimum, ∧/ for all-true. This replaces a large family of Ruby methods.Product (reduce with ×)
[1, 2, 3, 4, 5].reduce(:*) ×/1 2 3 4 5 The product reduction
×/ multiplies all elements together. For the first five positive integers, ×/1 2 3 4 5 = 120 = 5!. This means factorial can be expressed concisely in APL: ×/1+⍳n computes n! by generating 1 through n (adding 1 to the 0-based iota) then multiplying.Maximum and minimum
[3, 1, 4, 1, 5].max
[3, 1, 4, 1, 5].min ⌈/3 1 4 1 5
⌊/3 1 4 1 5 The ceiling-reduction
⌈/ returns the maximum of all elements, and the floor-reduction ⌊/ returns the minimum. The ceiling and floor functions are also dyadic: 3⌈5 returns the larger of 3 and 5 (which is 5), and 3⌊5 returns the smaller (3). These dyadic forms vectorize over arrays just like any other APL operator.Factorial via reduction
(1..5).reduce(:*) # 5! = 120 ×/1+⍳5 This example combines iota (
⍳5 = 0 1 2 3 4), adding 1 to shift to 1 2 3 4 5, then multiplying all with ×/. The result is 5! = 120. Chaining small array operations to express complex computations without variables or loops is the essence of APL's "array thinking".Running sum (scan with +)
numbers = [1, 2, 3, 4, 5]
numbers.each_with_object([]) { |x, acc|
acc << (acc.last || 0) + x
} +\1 2 3 4 5 The
\ operator (scan) is like reduce but keeps all intermediate results. +\1 2 3 4 5 returns 1 3 6 10 15 — the running sum at each position. Any function can be used with scan: ×\ for running product, ⌈\ for running maximum. Scan returns an array of the same length as the input.Running maximum
numbers = [3, 1, 4, 1, 5]
numbers.each_with_object([]) { |x, acc|
acc << [acc.last || x, x].max
} ⌈\3 1 4 1 5 The scan
⌈\ computes the running maximum: at each position, the value is the maximum of all elements seen so far. For 3 1 4 1 5, this gives 3 3 4 4 5. The running maximum is useful in problems like "the highest price so far" or "the best result to date". Ruby requires an explicit accumulator; APL expresses this in two glyphs.Higher-Order Operations
Apply a function to each element (¨)
[1, 2, 3, 4, 5].map { |x| x * x } {⍵×⍵}¨1 2 3 4 5 The
¨ (each) operator applies a function to each element of an array, collecting results. f¨array is equivalent to Ruby's array.map { |x| f(x) }. When used with a dyadic function and two arrays, left f¨ right pairs up elements: 1 2 3+¨10 20 30 = 11 22 33.Outer product (∘.f)
[1, 2, 3].product([1, 2, 3]).map { |a, b| a * b }
.each_slice(3).to_a 1 2 3∘.×1 2 3 The outer product operator
∘.f applies function f to every combination of elements from the left and right arrays, producing a matrix. A∘.×B creates a multiplication table. For vectors of length m and n, the result is an m×n matrix. The outer product works with any function: ∘.= creates an identity-matrix-like Boolean table, ∘.+ an addition table.Inner product (f.g)
# Dot product (sum of element-wise products)
a = [1, 2, 3]
b = [4, 5, 6]
a.zip(b).sum { |x, y| x * y } 1 2 3+.×4 5 6 The inner product
f.g first applies g element-wise between corresponding elements of the two arrays, then reduces with f. The classic dot product +.× multiplies corresponding elements and sums the results: 1×4 + 2×5 + 3×6 = 32. Matrix multiplication is written A+.×B, generalizing the dot product to 2D arrays.Commute (swap arguments, ⍨)
# Subtract from 10: right arg subtracted from left
10 - 3 # => 7
# What if we want: 3 subtracted from 10, written right-to-left? 3-⍨10 The
⍨ (selfie/commute) operator swaps the left and right arguments of a dyadic function. 3-⍨10 computes 10-3 = 7. Used monadically, f⍨x applies f with the same argument on both sides: ×⍨3 computes 3×3 = 9 (squaring without naming the argument).Reduce along a matrix axis
matrix = [[1, 2, 3], [4, 5, 6]]
matrix.map(&:sum) # row sums: [6, 15] matrix←2 3⍴1 2 3 4 5 6
+/[1]matrix The axis specifier
[n] on a reduce or scan applies the operation along a specific axis. +/[1]matrix sums along axis 1 (columns), giving row totals. +/[0]matrix sums along axis 0 (rows), giving column totals. Without an axis specifier, reduction operates along the last axis by default.Functions (Dfns)
Simple function (dfn)
double = ->(x) { x * 2 }
double.call(5) double←{⍵×2}
double 5 A dfn (direct function) is written as
{body}. Inside, ⍵ (omega) is the right argument. Dfns are first-class values and can be assigned to names with ←. Calling a named function uses simple juxtaposition: double 5, not double(5). The dfn can also be used inline: {⍵×2} 5.Two-argument function (dyadic dfn)
add = ->(a, b) { a + b }
add.call(3, 4) add←{⍺+⍵}
3 add 4 In a dyadic dfn,
⍺ (alpha) is the left argument and ⍵ is the right. A dyadic function is called with the left argument before the function name and the right argument after: 3 add 4. The same dfn can also be used inline: 3{⍺+⍵}4. APL functions are always either monadic (one argument) or dyadic (two arguments) — never more.Conditional guard in dfn
sign = ->(x) {
if x < 0 then -1
elsif x > 0 then 1
else 0
end
}
sign.call(5) sign←{⍵<0:¯1 ⋄ ⍵>0:1 ⋄ 0}
sign 5 Guards in dfns use the syntax
condition: result. If the condition is 1 (true), the dfn returns result immediately. Multiple guards are separated by ⋄ (diamond). The last expression (with no colon) is the default. Guards are evaluated left to right; the first true guard wins. This is APL's primary conditional mechanism inside dfns.Recursive dfn (∇)
def factorial(n)
n <= 1 ? 1 : n * factorial(n - 1)
end
factorial(5) factorial←{⍵≤1:1 ⋄ ⍵×∇⍵-1}
factorial 5 Inside a dfn,
∇ (del) refers to the dfn itself, enabling recursion. The factorial function checks whether ⍵≤1 (guard returning 1) and otherwise multiplies ⍵ by the recursive call ∇⍵-1. The right-to-left rule means this reads as ⍵×(∇(⍵-1)): multiply ⍵ by the factorial of ⍵ minus 1.Local variable in dfn
transform = ->(x) {
doubled = x * 2
doubled + 1
}
transform.call(5) transform←{doubled←⍵×2 ⋄ doubled+1}
transform 5 Assignments inside a dfn create local variables.
doubled←⍵×2 binds the name doubled locally; it does not affect the outer scope. The last expression in the dfn (doubled+1) is the return value. Unlike Ruby, there is no return keyword — the final expression is always the result.Dfn as argument to an operator
[1, 2, 3, 4, 5].map { |x| x ** 2 } {⍵*2}¨1 2 3 4 5 Dfns are first-class values and can be passed directly to operators like
¨ (each), / (reduce), and \ (scan) without naming them. {⍵*2}¨array squares every element. This corresponds to Ruby's anonymous block syntax { |x| x ** 2 } but is the primary rather than secondary way of passing functions.Trains & Tacit Style
Average as a fork
numbers = [1, 2, 3, 4, 5]
numbers.sum.to_f / numbers.length (+/÷≢)1 2 3 4 5 A three-function train
(f g h) called a "fork" applies f and h to the argument separately, then combines their results with g: (f⍵) g (h⍵). Here +/÷≢ computes sum divided by count, giving the average. The fork is a named function and can be assigned: average←+/÷≢.Atop — compose two functions
[3, 1, 4, 1, 5].sum * -1 (-+/)3 1 4 1 5 A two-function train
(f g) called an "atop" applies g to the argument first, then f to the result: f(g⍵). Here (-+/) sums the array with +/ and then negates with -. This is function composition reading right to left, matching APL's evaluation order.Square using ⍨ (selfie)
[1, 2, 3, 4, 5].map { |x| x * x } ×⍨¨1 2 3 4 5 When
⍨ is applied to a monadic function, it produces a derived function that uses the same argument for both left and right: ×⍨x computes x×x. Combined with ¨, this squares every element of the array. This is a common tacit idiom in APL — no need to write a dfn like {⍵×⍵}.Sum of squares
[1, 2, 3, 4, 5].sum { |x| x * x } (+/×⍨)1 2 3 4 5 The fork
(+/×⍨) squares all elements with ×⍨ and then sums with +/. This reads naturally: "sum the squares of the array". Tacit (point-free) style in APL chains operations without naming intermediate results or loop variables. It is the APL equivalent of a functional pipeline.Dyadic fork
# Difference of squares: (a-b) * (a+b)
(5 - 3) * (5 + 3) # => 16 5(-×+)3 A fork applied dyadically evaluates both outer functions with both arguments, then combines with the middle function:
(⍺ f ⍵) g (⍺ h ⍵). Here 5(-×+)3 computes (5-3) × (5+3) = 2×8 = 16. This is the difference-of-squares factoring. Dyadic trains enable expressing binary operations on two values as a single tacit function without naming anything.Gotchas for Rubyists
Right-to-left has no exceptions
2 * 3 + 4 # => 10 (Ruby: 2*3=6 first due to precedence, then +4) 2×3+4 APL evaluates strictly right to left with no operator precedence.
2×3+4 evaluates 3+4=7 first (rightmost operator), then 2×7=14. In Ruby, 2 * 3 + 4 evaluates 2*3=6 first (multiplication has higher precedence than addition), then 6+4=10. The same-looking expression produces 10 in Ruby and 14 in APL. Use parentheses to override APL's right-to-left order: (2×3)+4 gives 10.ngn/apl uses 0-based indexing
letters = ["a", "b", "c", "d", "e"]
letters[0] # => "a"
letters[1] # => "b" letters←'abcde'
letters[0]
letters[1] In ngn/apl, the index origin is fixed at 0. This matches Ruby, Python, and JavaScript. Traditional Dyalog APL defaults to 1-based indexing (first element at index 1), which is configurable via
⎕IO. When reading APL examples from books or the internet, check whether they assume 1-based indexing — many classic APL texts use 1-based conventions.* is power, × is multiply
2 * 8 # => 16 (Ruby: * means multiply) 2*8 The expression
2*8 means multiplication (16) in Ruby but exponentiation (256) in APL — the same syntax produces a completely different result. APL uses × (U+00D7, the actual multiplication sign) for multiplication and reserves * exclusively for power. In Ruby, ** is power and * is multiply. This is the most common trap for programmers coming from any other language to APL.Division always produces an exact result
7 / 2 # => 3 (Ruby: integer division — fractional part dropped!) 7÷2 In Ruby, dividing two integers performs integer division:
7 / 2 = 3, silently dropping the fractional part. In APL, ÷ always returns the mathematically exact result: 7÷2 = 3.5. To get integer division in APL, apply floor: ⌊7÷2 = 3. The APL approach avoids the common Ruby pitfall of losing precision when both operands happen to be integers.¯ is a literal, - is an operator
x = -3 # literal negative 3
y = -x # negate x (applies minus to x) x←¯3
y←-x
y In APL, the
¯ (high minus, written above the number) is part of a negative number literal. The - character is purely the subtraction/negation operator. Writing -3 in APL means "negate the number 3", just as -x negates a variable. Writing ¯3 means "the literal negative three". This distinction prevents parsing ambiguity in vectors like 3 ¯1 4.Booleans are integers, not objects
result = [1, 2, 3, 4, 5].select { |x| x > 3 }
puts result.length # count of matches numbers←1 2 3 4 5
mask←numbers>3
+/mask APL comparison operators produce integers (0 or 1), not Boolean objects. This means you can use Boolean results directly in arithmetic:
+/numbers>3 counts how many elements are greater than 3 without any conversion. In Ruby, you must call .count or .sum { |x| x ? 1 : 0 }. The Boolean-as-integer design is a deliberate and powerful choice.