PLACE |
languages |
In 1957, Kenneth E. Iverson devised a notation for description and analysis of computing-related topics. A characteristic feature of that notation was the lavish and inventive use of arrays of arbitrary dimensions. Iverson used it for some time in his teaching to university students. As other people got interested in the formalism, they also began using it for teaching and reasearch. Thus a community emerged around the language, experimenting with it and contributing to its design. At some time, the language became known as APL, for ‘A Programming Language’. In 1962, Iverson popularized it in a book under the same title (now freely available online).
APL was created as a tool for thought and exposition, its relation to actually programming a computer being only virtual. The successful use of the language for communicating mathematical and computational ideas created a critical mass of interest that eventually lead to implementing it. Starting from 1963, a series of increasingly more complete implementations appeared.
The fact that the design of APL evolved through heavy experimentation, which was being taking place long before using it on a computer, helped keeping the language practical while semantically at a very high-level. Adhering to these goals sometimes required significant changes to the language, hardly possible had an implementation been available and used at an early stage of design. Thus APL had the chance to reach relative maturity already during its paper phase. Still, it did evolve in the later years as well.
APL is one of the earliest examples of an interpretive and interactive computing environment, and these features greatly contributed to it being appreciated by its users and to its continuous success.
An important trait of the language is its expression orientation: APL's model of computation is strongly based on evaluating expressions to produce values, as opposed to executing commands for side effects. There are assignments and gotos in APL, but an arbitrary program tends to contain much more expressions than in a traditional imperative language.
The usual statements for sequence control, typical of imperative, statement-based computation are excluded from APL for their supposed complicating the language without providing much utility. Indeed, many instances of iteration are obviated by the presence of bulk (whole-array) operations. The same holds for recursion, although the language admits recursive calls. Binary-valued evaluation and making choices computationally, within an expression, often can replace conditional execution of statements. In addition, the only form of jump statement present in the language is general enough to provide multi-way conditional branches, including returning from a function.
Expression-oriented programming is strongly encouraged by treating collections of data – even if these are solely arrays – as wholes which can be passed as arguments to suitable operations and obtained as results from them. The abundance of operations ensures a multitude of combination possibilities for constructing expressions.
With the years passing, APL's expression style evolved into an even more definite orientation towards functional programming. Functions, initially only second-class objects in the language, gradually became fully fledged values. Some modern implementations even support closures.
One thing immediately observable about APL is that it uses a special character set to depict operations. Here are some non-ASCII characters of the APL alphabet (Unicode support is needed to see them in the browser):
⋄ ⎕ ⍞ ⌹ ∆ ∇ ⍋ ⍒ ⍫
× ÷ ★ ⌈ ⌊ ≤ ≥ ≠ ≢ ≡ ∨ ∧
← → ↓ ↑ ⊢ ⊣ ⊥ ⊤ ⊂ ⊃ ∪ ∩
∘ ○ ⍟ ⍤ ⍥ ⊖ ⌽ ⍉
⍺ ⍵ ⍴ ⍳ ∊
In a sense, even the letters of the latin alphabet are used in APL somewhat unusually. Traditionally they are printed or displayed on a screen in italics, in conformance with the way variable names are typeset in mathematical texts, and also to distinguish some letters from similarly looking other characters, e.g. L
from ⌊
and T
from ⊤
.
There are great many operations in APL. Most characters represent both a monadic and a dyadic function – this is a systematically followed design principle. Which one is in use in a particular context is determined by simple syntax rules.
APL expressions evaluate uniformly in a right-to-left direction, so there is no need for precedence or associativity rules to disambiguate the order of evaluation. Actually, the rule being postulated is that ‘the right argument of any function is the value of the entire expression following it’. This also implies that reading an expression left-to-right reveals its hierarchical structure, starting from the major operation – the one that produces the final value – and proceeding towards finer sub-expressions. Parentheses can still be used for explicit control when needed. For example, (a-b)×c-d+e
reads: ‘the product of a-b
and the expression that happens to be the difference of c
and d+e
’, or (a-b)×(c-(d+e))
in a fully parenthesized form.
Besides functions which operate on data and produce data, there are the so called operators. The latter take functions or data as arguments and produce functions. Unlike functions, there are only several predefined operators in most implementations of APL, and there is no possibility to define new ones. The language J, APL's most direct successor, removes this limitation, and so do some modern implementations of APL.
Syntactically, operators behave differently from functions: in any expression, the former execute before the latter, and monadic operators take their arguments from their left. For example, the expression +/m+.×v
sums the elements of the product of a matrix m and a vector v. The .
(dot) operator takes the functions +
and ×
as arguments and produces a function that computes the product. The resulting vector is acted upon by the function +/
(which can be called summation), obtained by applying the operator /
(reduction) on the function +
.
It should be noted that this recognition of operators as a special class of functions is not shared (indeed, considered unnecessary) outside the family of APL-like languages. Historically, operators were invented and introduced one by one in APL, and at some point it was decided that they form a sufficiently important, distinct family of program objects to merit introducig special rules for them in the language. Perhaps because APL was not designed as a functional language per se, no attempt was made, nor was it desired, to unify the treatment of ordinary functions and operators.
A more usual terminology for operators used elsewhere is higher-order functions, and in a language that can work with such functions – typically functional languages but also increasingly many others – they can be created as ‘ordinary functions’ and are not considered special in any way, syntactically or otherwise.
Primitive data is called scalars in APL, and is either numbers or characters. Instead of Boolean datatype, APL makes use of the numbers 0 and 1, for ‘false’ and ‘true’ (this is a technique well known now from C-like and other languages, but it did originate in APL). Arrays of 0s and 1s are especially useful.
All structured data is arrays. In fact, even scalars are considered arrays of zero dimensions. One-dimensional arrays are called vectors or lists, two-dimensional ones – matrices or tables, and arrays of higher dimensions are multiple tables.
APL is extraordinarily efficacious in array manipulation due to two reasons. One is the richness of functions readily available in the language – some of them specifically designed for arrays, others of general utility. More functions can be obtained on-the-fly by use of operators, or by function definition. The former method gives APL a distinctive applicative flavour, akin to functional programming.
The array processing functions of APL allow creation, reorganization, searching, extraction and replacement of arbitrary sub-structures, merging, derivation of related structures (such as consisting of dimensions, indices and binary values), and other operations on arrays – all dynamic and very versatile.
It should be taken into account that APL's functions and operators are heavily loaded with meaning, mainly through generalization. The designers of the language applied much thinking to ensure that. Among the many examples, the factorial function is actually implemented as the more general gamma-function; the ‘matrix divide’ function is also generalized to implement the least-square method, so that even over-specified linear equation systems can be solved. An impressive combinatorial generalization is presented by the ‘transpose’ function, one particular case of the application of which can be seen in the Examples section below.
The other source of APL's array processing power is the pervasive way functions are used with respect to arrays. Any function defined on scalars automatically extends its action to arrays when given such argument(s). For example, x+y
would mean adding two numbers if x
and y
happen to be numbers, while if, say, x
is a number and y
is an array, the same expression means adding x
to each element of y
. And if x
is a vector and y
is a matrix, then x
is being added to each row (or column) of y
etc. Where necessary, function application can be modified ‘rank-wise’, i.e. specifying which axes or cells in an array are to be operated on.
Because whatever dimensions an array has, ultimately it may only consist of scalars, and in this sense it is homogeneous both in its structure and in the type of its elements, a provision for building heterogeneous data structures was introduced in APL in the form of boxing. Any data item, i.e., any array, can be ‘enclosed into a box’, whereupon it effectively becomes a scalar. The elements of such an object cannot be examined or otherwise operated, unless that object is unclosed and therefore becomes a non-scalar again. Being a box and thus a scalar, however, makes it possible an array to be included – effectively nested – in other arrays. Thus, with proper boxing, arbitrary content can be put into a single array.
Boxing is also helpful for modelling multiple arguments to functions. APL functions, including user-defined ones, are restricted to being niladic, monadic, or dyadic. Therefore, whenever more than two arguments are needed, one has to resort to packing them into an array, including boxing as necessary.
As for user-defined functions, they are also uncommon in another way. Unlike most other currently used languages, but like Logo and the early implementations of Lisp, all non-local variables have dynamic scope, i.e. are resolved in the calling context rather than the textual one.
Each of the first several examples presents a single expression, which can readily be used in an unconditional direct definition of a function. The name ⍵
, or both the names ⍺
and ⍵
are used for the parameters of monadic and dyadic expressions, respectively. While names can be chosen arbitrarily when the expressions are self-contained, the use of ⍺
and ⍵
is mandatory if an expression is indeed the body of a direct function definition.
⍝ Shuffling a vector – breaking ⍵ down into ⍺ pieces from which ⍝ another vector is built by merging. E.g. if ⍵ is 'abcdefghij' ⍝ and ⍺ is 3, the pieces are 'abcd', 'efg', and 'hij', and the ⍝ result is 'aehbficgjd' ⍵[⍋⍋(⍴⍵)⍴⍳⍺]
⍳⍺
gives an index list of length ⍺
, which for ⍺=3
is 0 1 2
. Then ⍴
, with left argument ⍴⍵
, repeats the contents of that list as needed to form a list the same length as ⍵
. If ⍵
is of length 10, then the result is 0 1 2 0 1 2 0 1 2 0
. Next, the two applications of ⍋
produce the grading vector of the result so far, and then the grading vector of the grading vector. For the above value of ⍵
, these would be 0 3 6 9 1 4 7 2 5 8
and 0 4 7 1 5 8 2 6 9 3
, respectively. Finally, by means of the subscript operator []
, the elements of ⍵
are extracted in the necessary order to build the shuffled vector.⍝ The main diagonal of a square matrix ⍵, and more generally – of a hypercube ⍵, respectively 1 1⍉⍵ ((⍴⍴⍵)⍴1)⍉⍵
⍴
, as a monad, gives the vector of dimensions of its argument, ⍴⍴
is the number of those dimensions. Then the dyadic ⍴
produces a vector of as many 1
s to pass as a left argument to ⍉
. In general, the latter function transposes the axes of its multi-dimension-array right argument according to a permutation specified by the left argument. Here, a particular case applies: repeating values in the left argument select a diagonal along those axes – and we have ensured that this is actually all axes.⍝ A square matrix of order ⍵ that has 1s on and above its main diagonal and 0s elsewhere (⍳⍵)∘.≤⍳⍵
∘.
(outer product) operator, which produces a table of a function – in this case the comparison ≤
– for a given pair of vector arguments. Both arguments are the same increasing sequence ⍳⍵
, hence the result.⍝ A function that finds the list of Fibonacci numbers, ⍝ up to specified length (argument k) ∇ f ← fib k [1] f ← ,1 [2] → (k=⍴f)/0 [3] f ← f,+/¯2↑f [4] → 2 ∇
f
to be the result of the function fib
, which happens to be monadic with an argument called k
, defining how many items we want in f
. The main computation is on line 3: f
's two trailing numbers are extracted into a two-element vector (¯2↑f
) and added together (+/
), giving the new item to be appended (,
) to f
's contents. The so obtained vector is assigned (←
) as a new value to f
. Line 4 is a jump to line 2, where it is checked whether f
already contains enough items, and if so, a jump to 0 is performed, which, as 0 is not a valid line number, is equivalent to return from the function. If the length of f
is less than k
, the selection operation (k=⍴f)/0
gives an empty vector, a jump to which is by definition a no-jump, so the execution continues in normal order, at line 3. Initially, f
is given a vector of one element, 1
. Note that even for that f
the expression ¯2↑f
has a correct value.⍝ A so-called ‘direct’, recursive definition of a Fibonacci function fbr: f,+/¯2↑f ← fbr ⍵-1 : ⍵=1 : 1
⍵
is the obligatory argument name for a direct definition, and f
is a local variable. The part after the second :
is a condition which determines whether the value to be returned shall be produced by the expression on the right, or the one on the left. The latter expression contains a recursive call of the function fbr
to itself, the result of which is handled similarly to the way the above non-recursive version of the function does.As a programming language, APL is popular in finance, insurance, mathematical and science experimenting and sumulations, and engineering, and is considered a highly effective rapid prototyping tool in even more areas. Also, being array-oriented, APL has great potential for exploiting SIMD and multi-core computer architectures.
Several companies have been closely related to the commercial use and development of APL. Among them, IBM has a special place. The early development of APL took place at IBM, and this is also where the first implementation of the language was made. As a result of an experiment called SCAMP, a desktop calculator – an interactive APL programming device, was created in 1973, followed in 1975 by the first IBM personal computer, IBM 5100 – a system intended for the educational market which had APL optionally installed. (Another, much smaller company, also succeeded in creating their own personal computer, MCM/70, with a built-in APL system – even a bit earlier than IBM!)
However, APL's principal author was convinced – something he expressed in numerous publications – that APL's most important use was ‘as an executable notation for the teaching of a wide range of subjects’, mathematics in particular. According to him, APL was to develop into a streamlined mathematical notation – inambiguous, free of singularities, and with the executability and universality characteristic of a programming language.
In the late 1980s, Iverson's evolution of ideas about APL required changing the language so substantially that eventually he created a new one, J. Other array processing languages were also heavily influenced by APL, or even directly descend from it. A very successful language of this kind is K.
Iverson experimented in teaching mathematical disciplines, such as algebra and calculus, using APL (and later J) in place of the usual mathematical notation. He used APL and J in writing a series of books in the said disciplines.
The work of K. Iverson on APL, in particular – showing a style of programming that emphasizes the processing of whole arrays rather than individual items, and that makes use of operators to create functions from given functions – was part of the motivation for J. Backus to create FP. The applicative style, promoted by FP, was in turn influential in shaping modern functional programming. Thus today's functional programming originates, in part, from APL.
A programming language: a book describing an early version of APL; available online as a scanned copy and in Web format
Vector (or this way): the web page of the Journal of the British APL Association. Provides links to APL (and its descendants) resources
The APL Wiki page: some examples, resources
The FinnAPL Idiom Library: a collection of idiomatic phrases solving a number of problems
The APL Idiom List: an older collection of idiomatic solutions (Perlis & Rugaber)
Mastering Dyalog APL: a book on APL programming, available both in print and online (PDF)
boykobbatgmaildotcom