https://medium.com/@kaleberg7/apl-an-array-oriented-programming-language-b03fb39401c1 [ ] A Kaleberg A Kaleberg Follow Nov 27, 2018 * 14 min read APL: An Array Oriented Programming Language APL was invented by Ken Iverson in the early 1960s as a notation for array oriented calculations. It was precise and useful enough, so it was implemented as a programming language, one of the old ones and one of the more obscure ones. The 1950s and 1960s were a golden age of language design. So many ideas in modern programming languages were first developed in those early formative years. For example: 1. FORTRAN and Algol set the standard for many languages and most modern languages use precedent based expressions and distinguish between expressions and statements. 2. LISP bared the structure of computation with programs as a first order data type and there has recently been a round of rediscovery with languages adopting features like garbage collection, first class procedures and linearized data representation. 3. COMIT and SNOBOL are still present in languages like Perl and anywhere regular expressions are used for string matching. 4. COBOL is still used in business oriented data processing and there are countless "report generation" languages and packages sharing COBOL's presentation oriented data philosophy. 5. FORTH, as stack oriented language, lives on as Postscript and SVG for graphical presentation. The primitive reptilian brains that evolved in the formative era still pulsate in the modern organism. In contrast, APL has almost vanished. There are implementations. There is GNU APL, and Dyalog has produced a modernized version. There was a brief APL revival back in the 1980s where many of the language's features were adopted for programming the Connection Machine. The last Connection Machine I saw was at the Museum of Modern Art, and while its numerous LEDs were still blinking, it's internal architecture and programming paradigm have been all but forgotten. We are in a new golden era of computer language development. I'll call this is the silver age with new languages like Python, Ruby and Javascript built and developed on the foundations of the golden age but offering greater convenience and expressibility. These languages have been informed by FORTRAN, Algol, LISP, COMIT and others. They offer the best features of golden age languages in modern packages to support modern software developers. Many of these features were ahead of their time and are only now being recognized and incorporated as processors and systems have grown more advanced and more powerful. In contrast, APL has been neglected. It was way too far ahead of its time, but it might be time to reassess this. One big development in modern computing has been the widespread adoption of SIMD processors. SIMD stands for single instruction multiple data. In conventional processors there is a single instruction flow, and each instruction manipulates a single set of values. An add instruction might add two numbers to get a third. In a SIMD processor an add instruction might add two arrays of numbers to get a third array. If you have a lot of numbers to add, that sounds great, but where are these processors today? As it turns out, SIMD processors are everywhere. Graphics processors, GPUs, are SIMD, and they can perform the massive computing needed for 3D graphics, compression, real time composition and other processor intensive operations. Even a modestly priced laptop or smartphone has more computing power in its GPU than would be found in a 1990s supercomputer center. The problem with GPUs, however, is that they are hard to program. Most of the code that they run has been specifically developed to take advantage of the raw power available. No one writes Javascript code to run on a GPU, and there really isn't a language that makes programming GPUs as easy as using Javascript in a browser. The first SIMD processors were built back in the 1960s. The Illiac IV was probably the first supercomputer to support SIMD processing. It had a primary conventional processor and an additional 64 floating point units that were controlled by a single instruction stream. In the early 1970s, when it was finally completed, it offered 50 MFLOPS. In a sense it was the first of its kind, and it would be decades before SIMD processors would become common. Meanwhile, APL lurked in the shadows. It had a bit of a cult following, but never moved into the mainstream. One of the few signs of its influence was in a language for programming the Connection Machine, a highly parallel procesor for artificial intelligence developed in the 1980s. The Connection Machine had tens of thousands of processing elements but a single instruction path. Danny Hillis, its inventor, developed a programming language for the machine that supported "alpha" parallelism and "beta" reduction. One had to think in terms of an intrinsically parallel data type, the "xector", to program it. These ideas appear today in modern parallel programming systems that use map and reduce. The primary influence was from LISP, but Guy Steele, who developed the programming language for the commercial form of the Connection Machine, had written an APL interpreter in LISP and almost certainly adopted ideas from APL into the new programming paradigm. There really isn't much more historical background. Perhaps it is time for a bit of time travel. We'll set the dial back to 1970 and find ourselves in a small room with an IBM 2741 terminal, an antique, but stylish, teletype connected to an antique computer, perhaps an IBM 360 running CP-67 and an APL subsystem. This may be hard to imagine nowadays. Old fashioned computer terminals were just automatic typewriters. They had a keyboard and a platen and a ribbon for ink. Unlike a typewriter, paper was fed from a box or roll rather than in single sheets, but otherwise, an IBM 2741 looked like a typewriter. The IBM 2741 was different from most typewriters in that it had a single "golf ball" print head rather than individual striking elements for each letter. The "golf ball" would turn and tilt, then slam the the inked ribbon against the pag and print a character. This had advantages. Unlike a more conventional typewriter where each key had its own mechanism, there was only one moving part, the "golf ball". This made it easier for a computer to control. Even more amazing, the "golf ball" could be replaced with another "golf ball", so the typewriter could change fonts. If you were typing with the Roman alphabet and wanted to type something in Cyrillic, you just changed the "golf ball". We take the ability to shift fonts for granted these days, but IBM had done something pretty spectacular. The point of this was that APL used its own special character set. There was a special APL "golf ball" and a special keyboard mapping for it. APL had started out as a mathematical notation, but the "golf ball" helped it become a computer language. Mathematicians had been using symbols like little arrows, triangles and comparison operators for ages, but computers back then couldn't handle things like a greater-than-or-equal sign or even things like curly braces. This meant languages like FORTRAN used awkward notations: greater-than-or-equal was rendered ".GE." It worked. It was useful. It was ugly. The whole point of APL as a programming language was to turn a good looking mathematical notation into a good looking programming language. Turning its special characters into EBCDIC compatible sequences would have defeated its aesthetic purpose. That's enough preamble. Now for some APL. APL had two data types. There were numbers and there were characters. It had one aggregate data type. If a datum wasn't a scalar, it was an array. There were no structures, sets, hash tables or classes. APL was about arrays. APL was about thinking different. One glaring difference was that APL used an unusual numeric notation for numeric constants. Most computer languages use the same character to indicate negative numbers as for negation or subtraction. APL had a special character to indicate negative numbers: - as in -3.5. That meant one could type in one dimensional array values as a sequence of numbers. For example: 1 -2 3 -4 is a one dimensional array of length four containing the values one, minus two, three and minus four in that order. Using a more conventional notation for negative numbers would have introduced ambiguity. A single number appearing alone would be a scalar. A sequence of numbers would be one dimensional array. Characters were delimited by single quotes, as they were in many languages. A single character in quotes would be a scalar. Multiple characters in quotes would be a one dimensional array of characters. APL has the usual arithmetic operators, but the symbols for multiplication and division are the characters familiar from arithmetic, not from programming languages. Most modern languages use an asterisk for multiplication. APL uses a multiplication sign as in 2x3. Division is indicated by the familiar division sign as in 2/3. In APL, most operators have a monadic, single argument form, and a dyadic, two argument form. Some of this should be familiar, but APL adds a few twists. For example, monadic division returns the inverse, so that: /4 - 0.25. The arithmetic operators work on scalars as one might expect: 2+3 - 5 4/5- 0.8 The operators also work on arrays, even the dyadic form as long as the two arrays have the same dimensions: 1 2 3+3 -5 4 - 4 -3 7 -1 0 -1- -1 0 1 The operation is applied to each element for a monadic operation and to each corresponding pair of elements for a dyadic operation. One can also use a dyadic operator with a vector and a scalar: 1 2 3/7- 0.1428571429 0.2857142857 0.4285714286 7+ 1 2 3 - 8 9 10 Already, one can see how this encourages thinking in terms of parallel processing. Since array operations are implicit, they could be compiled into efficient SIMD code. APL encourages array based SIMD thinking by making it a central, natural part of the language. APL has a lot of operators, most having both a monadic and dyadic form. Operator/Monadic/Dyadic +/Identity operator/Addition -/Negation/Subtraction x/Returns the sign as -1 0 1/Multiplication //Inverse/Division */Exponential, power of e/Raise to the power [?]/Natural logarithm/Log in a given base |/Absolute value/Remainder [?]/Ceiling, round up/Maximum value [?]/Floor, round down/Minimum value = [?] <= < > >=/No monadic form/Comparison operators [?][?]/No monadic form/Logical or, and !/Factorial or gamma/Combinatoric C ?/Random integer/Deal a random hand The comparison operators return 0 for false or 1 for true much as they do in C and related languages. The logical and and or operators treat zero and negative numbers as false and positive numbers as true. With all these operators, operator precedence becomes a problem. Does one perform a maximum before a multiplication? It could get very complicated, but APL finesses the issue. The order of evaluation is strictly right to left unless modified by parentheses. This means that: 2x3+4 - 14 APL adds 3 and 4, then multiplies the result by 2. Ordinary expressions might look cryptic, but exotic expressions will be comprehensible if one keeps this rule in mind. APL may seem exotic, but it does have ordinary things like variables which can be strings of letters and numbers as long as the first character is a letter. Assigning a value to a variable is no different than in most familiar programming languages: abc-3 4 5 OK, the use of a left pointing arrow is a bit exotic. The result of an assignment, considered as a dyadic operator, is the value assigned. The example above would yield a three element array containing 3, 4 and 5 in order. Let's consider a simple example. Suppose we want to composite two images stored in the variables imaginatively named img1 and img2. Perhaps we are fading from one image to another, so we have the fading factor, a number between 0 and 1, stored in the variable fade. We can composite the two images with the expression: (img1xfade)+img2x1-fade Evaluating from right to left, APL computes 1-fade, a scalar. Then it multiplies each element of img2, the second image, by that number. The next part of the expression is in parentheses, so APL then multiplies each element in img1, the first image, by the fading value and adds that array to the array computed using img2. There is no iteration, no looping. There is nothing about the dimensions of the images. This would work on two dimensional gray scale images or on three dimensional color images where each pixel might have three RGB values or four CMYK values. It could even be used for a volumetric grayscale fade or a volumetric color fade. APL is based on array notation, so the details of the array structure are hidden. Although APL is an old language, this notation could be compiled efficiently for a modern SIMD processor. I doubt anyone has such an APL compiler, but anyone who programs such systems will recognize a natural fit with the APL thought process. One technique for rapid rendering is to use what is called a Z-buffer. A Z-buffer records the distance of each pixel in a rendered image from the camera. Suppose each image, img1 and img2, in the above example had a Z-buffer with a distance value for each pixel. Combining the images properly requires using the closest pixel value from one image or the other. In APL this could be done with a simple expression: (img1xzbuf1>zbuf2)+img2xzbuf1<=zbuf2 The comparisons of zbuf1 and zbuf2 return arrays of the same size and shape as the original Z-buffers, but containing only 0s and 1s corresponding to which Z-buffer contained the closer or farther pixel. The images are combined using multiplication so that either a pixel from img1 or img2 is chosen. Programming in APL involves thinking in terms of array operations, operations that are intrinsically parallel. So far, it has all been about the map part of map and reduce. APL also has an operator for reduction, reducing the dimensionality of as a result. The reduction operator, written as a slash, takes another APL operator and turns it into a reduction operator. This is best explained with some examples: +/1 2 3 4 5- 15 [?]/2 -1 3- 3 In the first example, the addition operator is turned into a summation operator which sums the array. In the second example, the maximum operator is turned into a maximization operator which finds the largest value in the array. This would be a good time to discuss APL array structure. APL supports arrays with lots of dimensions, but APL array dimensions can be zero, so it is possible to have a three dimensional array that is 3 x 0 x 4. It would have no elements, but it would have a shape, so it could match the shape of other arrays. APL is sometimes fussy about the shapes of the arrays it will work with. There is an operator for dealing with the shapes of arrays. Its monadic form returns the shape of a value, and its dyadic form produces an array of the desired shape. The monadic form works like this: [?]3- [?]3 4 5- 3 The shape of a scalar is a one dimensional array of length zero. That's why there is nothing to the right of the arrow. The shape of a one dimensional array is an array with one element, the length of the original array. The dyadic form of the reshape operator lets one build and reshape arrays, changing the size and the number of dimensions. The first argument is a one dimensional array containing the dimensions, and the second argument is a scalar or array which is repeated to create an array of the desired shape with the desired content. We can create a 4 by 5 array full of random values between 1 and 10 rather simply: z-?4 5[?]10 That question mark is the random number operator, so each value in the resulting array would range from 1 to 10. The resulting array might print out as: 4 3 10 3 6 3 6 8 10 4 1 7 1 8 10 7 1 3 9 9 Your random numbers might be different. We can use reduction to take the sums of the rows or the columns in a two dimensional array: +/z- 28 18 31 35 +/[1]z- 16 29 20 28 19 +/[2]z- 28 18 31 35 The default form takes the sums of the rows. It turns a 4 by 5 array into a one dimensional array of length 4. APL reduction can also reduce along other axes, so it can be used to take the sums of the columns. APL is no ordinary computer language, and its origin as an array notation permeates. There is another operator that APL programmers find useful, the iota operator for generating a list of integers from 1 to a specified value. This operator makes it easy to evaluate series. For example: +//!-1+[?]20- 2.718281828 That's how one would compute the value of e using a Taylor series in APL. Evaluating from right to left, APL would generate a list of the integers from 1 to 20. Then it would subtract one from each of them. Then it would take the factorial of each of those values. Then it would take the inverse of each of those factorials. Finally it would take the sum of that array with the result shown. Reduction isn't the only APL operator that modifies another APL operator. One useful operator, the inner product operator, combines two operators. It can be used to turn the addition and multiplication operators into a matrix multiplication operator. Consider the example: a-3 2[?][?]6 b-2 3[?][?]6 a+.xb - 9 12 15 19 26 33 29 40 51 This should not be surprising. You have probably already figured out that you could do a dot product of two one dimensional arrays using + /axb. However, once one starts thinking in terms of array operations, other applications come up. For example, searching: txt-3 10[?]'abcdefghijklmnopqrstuvwxyz1234' This would set the variable txt to a two dimensional array: abcdefghij klmnopqrst uvwxyz1234 If we use the inner product operator to combine the logical and with the equal comparison operator, we test each column against a particular string: 'dnx'[?].=txt -- - 0 0 0 1 0 0 0 0 0 0 APL encourages this type of thinking. There is a lot more to APL. There are other operators for modifying and combining operators. There are user defined functions that can be combined using APL operator combining operators. There is workspace management, searching, sorting and a host of other things. One glaring omission is the lack of control structure. Where the loops? Where are the conditionals? For the most part, the loops are embedded in the array operations, and the conditionals embedded in the logical operators. To be fair though, APL does have one explicit operator for control structure. The right arrow operator is used as a go to operator. That's right. APL has a go to operator, but no other control structures. If that doesn't encourage thinking in terms of array operations, nothing will. Even the go to in APL is weird. User defined functions usually execute each line of the definition in turn. Lines may have labels indicated by a terminating colon character as is done in many assembly languages. Those label definitions define integer constants by line number. The go to operator is monadic and takes an argument, the line number. If the line number is a scalar, then execution continues on the line as numbered. If the line number is an array of length zero, that is, an empty array, then the go to operator does nothing and the next line is executed. There you have it, looping and conditionals, unless you want to exploit the array operations. APL is a curious throwback, but it presents a challenge to the way one thinks about computing. It embeds its parallelism in its arrays, much as the Connection Machine embedded its parallelism in its xectors. APL can be used as a production language. It is surprisingly powerful for both numeric work and text processing. A friend of mine even wrote a LISP interpreter in it. Still, APL is probably more useful as a puzzle, a language that challenges one to think in a new way and provides the tools for exploring this unfamiliar way of thinking. Programming Computer History Parallel Computing -- -- 1 More from A Kaleberg Follow AboutHelpTermsPrivacy --------------------------------------------------------------------- Get the Medium app A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store Get unlimited access A Kaleberg A Kaleberg 169 Followers Follow Help Status Writers Blog Careers Privacy Terms About Text to speech