QL/APL – popularising APL
Vector 1:4, page 81
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 81
Background¶
APL has been available as a working computer programming language for nearly two decades. During that time, it has attracted a very enthusiastic band of users, and, to those who know APL, the advantages of the language over conventional programming languages are evident and very considerable. These advantages include:
- Faster development time;
- Elegance and self-consistency;
- Modularity;
- Excellent environment (the workspace) for building up a system in manageable parts;
- Unsurpassed debugging facilities;
- Freedom from annoying restrictions imposed by computer hardware;
- Good speed of execution compared with other interpreted languages;
- Fairly good portability of code.
The list is not exhaustive, but the overall message is clear: many APL users, including the author, regard APL as so superior to languages such as BASIC that they would never even consider using other languages if they could possibly help it.
Many commentators have considered this issue in the past, and it is indeed a vital question: If APL is so wonderful, why is it that APL accounts for such a small proportion (probably well under 1%) of computer language use? And in particular, why is it that a language of such obvious shortcomings as BASIC should be orders of magnitude more popular than APL?
The factors impeding the spread of APL¶
A full discussion of the factors which have led to the (in world terms) unpopularity of APL is beyond the scope of this paper (see John Myrna’s paper ‘Increasing the Use of APL’ in the APL83 proceedings). There are a number of explanations, of which two seem to be paramount:
- APL has tended to be available only on machines which are more expensive than comparable systems running BASIC;
- The APL character set is a disadvantage because it is at first sight off-putting to potential users, and because it involves use of non-standard peripherals.
This paper describes work which addresses both of these points. An APL interpreter has been implemented on a very low-cost, mass market machine, the Sinclair QL, in a version which uses a keyword syntax which is directly aimed at converting users from BASIC to APL. (For existing APLers, a version of the interpreter using APL symbols is also available for the QL).
Keyword versions of APL¶
In the past, there have been a number of proposals for versions of APL which did not require the use of the special APL character set. Of these, the STSC keyword system for APL*PLUS/PC is probably now the best known, and our initial idea was to retain compatibility with this system in our own interpreter. However, previous keyword versions of APL have, in the view of the author, been started from the wrong premise. The reasoning, if for example we take the STSC product as an example, seems to have been as follows:
- We have produced an APL interpreter for the IBM PC, which uses normal APL characters via a special character generator PROM.
- However, not all machines which might use the interpreter will have the special PROM fitted.
- Therefore we will provide a keyword option which will allow APL programming on standard machines.
The result of this way of looking at the problem is a keyword scheme which is fine for users who already know APL, but is not helpful in popularizing APL for people who have never seen an APL keyboard. There are a number of problems with the STSC approach:
- A single APL symbol can be expressed in a number of different ways. For example, the rho symbol can be entered as #shape (Monadic form), #reshape (dyadic form), or #rho (ambivalent form). # is used as a delimiter to distinguish keywords from user-defined names, but this can be switched off. Thus, in the STSC keyword system, the APL symbol rho can appear in six different forms. This we felt to be over-complicated for our purposes.
- As will be seen from the above example, the STSC choice of keywords is not always meaningful to users not familiar with an APL keyboard. Further examples are #theta and #domino.
-
In common with other proposed keyword systems, the STSC approach is to use ASCII symbols if possible to correspond with APL symbols. For example, ~ is used for high minus, giving problems when negative numbers are displayed:
This approach also gives rise to a scheme which is not regular in its approach to which functions have keyword forms. If we are to have a keyword system, then we should use keywords for all primitive functions, except for the most commonly used (plus, times, multiply, divide). It makes little sense to have the rarely-used random-number generator as a single symbol ?, if multiply is to be a keyword.
The STSC keywords are, we felt, often too long. The encode symbol, for example, becomes ‘represent’.
A different approach¶
Having studied various schemes for keyword APL interpreters, we eventually (and reluctantly) decided that, if a keyword APL was to be a tool to popularize the language, then a completely fresh approach was mandatory. The following criteria were established:
- The keyword system should be viable in its own right as a programming language, on the assumption that the user will never see an APL keyboard.
- It should as far as possible build on users’ experiences with other languages, particularly BASIC.
- It should be as far as possible consistent with APL, and certainly should not violate APL’s unique elegance and internal regularity.
- It should use the standard ASCII character set, but not make use of those ASCII symbols which differ in other national languages. For example, the ASCII square brackets [ and ] become special letters in Swedish or Danish, and it would make the language almost unusable for those countries if we employed such symbols.
- It should be as concise as possible without compromising the readability of lines of code.
- As far as possible, applications written in standard APL should be transportable to the keyword version.
- The number of keywords should be limited.
- To ease entering of functions, input should be case-insensitive, although we have chosen that on output keywords and user-defined names should be distinguished.
- For practical reasons, we wanted to make the specification simple to implement starting from an existing APL interpreter.
The final result¶
It is not possible to find a perfect solution to the problem of making a keyword version of APL, but after considerable work and experimentation, we believe we have come up with a version which is much better than merely acceptable; indeed, APL programmers with very considerable experience have found it very easy to take to. It is difficult to appreciate the flavour of the new version of APL without actually trying it at some length, but the following details should make clear the approach we have taken.
Case handling¶
The handling of upper/lower case is as follows:
- Comments and text strings can of course be entered using any valid ASCII characters, and are left unchanged.
- Lines of APL can be entered in either case, and case is ignored on syntax analysis (including in checking for keywords and Quad names).
- Keywords are displayed in LOWER CASE.
- Quad names are displayed in UPPER CASE, with # replacing Quad.
- All user-defined functions and variables are displayed in UPPER CASE.
This method of handling case implies that there are reserved words in this language. Alternatives, such as preceding keywords by a delimiter or making a distinction between upper and lower case input, were felt to be ergonomically unacceptable. However, there are only 47 reserved names in the specification, most of which are unlikely names for user-defined functions or variables, and in practice this has not caused any difficulties. By contrast, Microsoft’s BASIC for the IBM-PC has no fewer than 159 reserved words.
The distinction between standard (upper-case) letters and underlined (here lower-case) letters for APL names has been dropped. This partly because it was felt to be extremely confusing and tedious to have to use the shift key when entering lines of code, and partly because it would not ease readability if, for example, XX, xx and Xx were distinct APL variables.
Choice of keywords¶
The choice of particular keywords is of course largely a matter of taste, and there is no ideal solution. The following criteria were used in selecting keywords:
- They should be short (preferably less than four letters).
- They should be English words, rather than mnemonics or abbreviations.
- They should ease readability of code.
Some compromise was necessary, and the final choice is shown in the table.
One additional primitive was added to make the language easier for novice users. This is the IF operation, which is the same as the standard APL compress function, but with the arguments reversed. This makes it possible to enter function lines such as:
Choice of symbol equivalence¶
The ASCII character set is badly thought out as regards availability of arithmetic symbols. The absence of multiply and divide signs is particularly unfortunate. However, the ASCII character set is one thing which cannot be changed, and so we have to adapt to it. Clearly, use of keywords for multiply and divide would be unacceptable because of the frequency with which they are used, and the fact that users will expect symbols for these functions. We therefore applied our overriding criterion - the convenience of users upgrading from BASIC - and settled on * and / for multiply and divide. The same criterion was used in selecting the symbol for comment.
The choice of symbols for the APL square brackets [ and ] was a tricky one. Although these symbols do exist in the ASCII character set, they vary in the standard ISO national character sets, and so they were ruled out. The same is true of { and }. We therefore adopted angle brackets < and >, which are available and do not vary. Although it might appear at first sight that these symbols should be reserved for their APL use (greater than and less than), we wanted regularity in our implementation. Since there are no ASCII characters for ‘greater than or equal’, ‘less than or equal’, and ‘not equal’, we had to choose keywords for these, and we felt that we should therefore choose keywords for all the comparison functions. Thus, we selected GE, GT, EQ, LT, LE and NE for these functions (to be consistent with Fortran if not with BASIC), leaving angle brackets free for indexing. This also left free the = sign, which we considered using for assignment. However, in this case we could not swallow our natural distaste for this choice, and so the = sign was left unused, with the keyword ‘is’ selected for assignment.
The choice of a symbol for Quad was also tricky. Although we could have chosen different equivalences for Quad on its own, and Quad as part of a system name, we were not keen to do this. The use of the # is fairly natural as an equivalent to Quad, and we eventually made this choice. This was the only case where we relaxed our requirement for invariance of the symbols used across the different ISO national character sets, for # sometimes prints as a U.K. pound symbol on U.K. and French terminals.
A full list of the valid symbols in keyword APL is shown in the table at the end of this paper.
Minus sign handling¶
When this project was begun, we feared that the problem of the minus sign would be almost intractable. We certainly wanted to use the ASCII minus sign for subtraction, but it was also essential in our view that negative numbers should be entered and displayed using the same symbol. To have reports which produced negative numbers using any other symbol would rule out the use of the language for serious applications. Thus we were faced with the problem of putting back the confusion which APL had neatly avoided in distinguishing subtraction from negative numbers.
In practice, the problem turned out to be easily and very naturally solvable. We simply applied the rule that the minus sign immediately preceding a digit would be interpreted as the APL high minus, provided it was immediately preceded itself by a space. All other instances of the minus sign are treated as subtraction. This has proved to be completely natural in use, and unambiguous, and we are completely happy in this choice. The following examples should indicate how it works in practice:
Implementation¶
The keyword system described here has been implemented in versions of APL.68000 for Mirage-based systems, and for the very low-cost Sinclair QL. The implementation was done by modifying the syntax analysis portion of APL.68000 to look for keywords, as well as making a number of other minor changes in the interpreter and supervisor to deal with certain special cases. Keyword checking is done for the execute primitive, and Quad-FX, as well as for user-entered lines of code.
The internal representation of APL functions written using this method is identical to that of the conventional APL version. This means that a workspace, saved using standard APL.68000, can be loaded and run using the keyword version. There are cases where this will not work, but these cases are relatively rare, and the changes necessary to allow interchangability between the two versions of APL are not very significant. The intepreter uses the same rules for printing out lines of code with keywords as it does for standard APL names, so that at the ends of lines, keywords will not be split into two.
User reaction¶
Extensive work has been done in giving users the opportunity to comment on the keyword implementation. We have canvassed both experienced APL programmers, and people who have never used APL. The reactions have been almost unanimously very favourable indeed. We have been particularly pleased by the response of the experienced APLers, who might have been expected to have been negative about the various compromises which had to be made, but the following excerpt from one of our beta-test reports indicates the sort of response we received (this was written by a user who has around 7 years’ practical experience in the commercial use of APL, mainly on IBM mainframes):
“For comparison, I selected an existing sales statistics analysis system written in VSAPL. Translation was troublefree, being simply a matter of replacing symbols… In use, there was no discernible difference between the mainframe and QL versions… As for the general flavour of keyword QL/APL, I liked it immensely - much to my amazement… Since starting to use keyword QL/APL, I have become convinced that the conventional APL character set was just an elitist trick to restrict the spread of an immensely powerful language.”
The Future¶
We believe that keyword QL/APL represents a major breakthrough in the effort to bring APL out of obscurity. As far as we know, this is the first time that a keyword version of APL suitable for people unfamiliar with the language has been released.
As far as the particular implementation of the language on the QL is concerned, here again we believe that a significant breakthrough has been made. This is due not only to the price/performance ratio - QL/APL costs under 100 pounds including VAT, making it possible to purchase a working APL system for under 500 pounds - but also to the excellent facilities which we have been able to include in the interpreter. Foremost amongst these are very good built-in colour graphics, and support for multiple windows on the screen.
Clearly, the important thing is to get large numbers of people familiar with the language, and we certainly hope that this version of keyword APL will become a de facto standard, simply by virtue of the large anticipated volume of sales in Europe and the US. It will then be up to the APL community as a whole to build on that base.
Appendix 1: The symbols in traditional APL and the QL/APL keyword equivalents¶
FHOLD (file hold) ON /
FDROP (file drop) OVER \
FGET (file get) PICFMT ⍺
FPUT (file put) DECODE ⊥
NOR ⍱ MIN ⌊
NAND ⍲ IN ∊
DOWN ⍒ DEFN ∇
UP ⍋ INDEX ⍳
ROTATE ⌽ OUTER ∘
TRANS ⍉ ABS |
LOG ⍟ ENCODE ⊤
LOCK ⍫ TRIG ○
EXEC ⍎ EXP *
FMT ⍕ RAND ?
PROMPT ⍞ SIZE ⍴
FACT ! MAX ⌈
MATDIV ⌹ NOT ~
LT < DROP ↓
LE ≤ IF ω
EQ = TAKE ↑
GT > IS ←
OR ∨ GOTO →
AND ∧ GE ≥
NE ≠
Appendix 2: Example function written in keyword APL¶
To show how keyword APL looks in a real example, we have translated Professor Hawkes’ Numerical Integration function from page 122 of Vector, Vol 1. No. 2. The function is also reproduced below. It is interesting to compare the two listings side by side. The style (for example in branching method and the use of underlined letters and delta) has been changed very slightly to illustrate stylistic features of keyword QL/APL, although this is not necessary.
defn R is Y NUMINT X;N;RANGE;RT;DX;GRID;FO;FE;M;S
<1> ! Numerical Integration. Y is a string of form 'A FNNAME'
<2> ! The global INTERR specifies accuracy
<3> ! Result is integral over N subintervals of X<1>, X<2>
<4> XCHECK: goto XOPTIONS if (1 ge size size X) and 3 ge size ,X
<5> XMESS: 'X should be vector of shape 3. X<3> is number of intervals'
<6> 'into which (X<1>, X<2>) divided.'
<7> 'Default X<3> is 1 if not specified.'
<8> 'If only scalar X given then it is taken as X = 0,X,1' & goto 0
<9> XOPTIONS: goto TWO if 1 le size ,X
<10> X is 0,X
<11> TWO: goto XCHECK2 if 3 eq size X
<12> X is X,1
<13> XCHECK2: goto XMESS if X<3> ne 1 max min X<3>
<14> M is 1
<15> R is (N is X<3>) size 0
<16> RANGE is X<2>-X<1>
<17> LOOP: RT is R
<18> M is M*2
<19> DX is RANGE/S is M*N
<20> GRID is X<1>+DX*0,index S
<21> FO is exec Y,' GRID'
<22> GRID is X<1>+DX*(index S)-0.5
<23> FE is exec Y,' GRID'
<24> R is (DX/6)* +on(N,M)size(4*FE)+(1 drop FO)+ -1 drop FO
<25> goto LOOP if INTERR lt max on abs R-RT
defn
∇ R←Y NUMINT X;N;RANGE;RT;DX;GRID;FO;FE;M;S
[1] ⍝ NUMERICAL INTEGRATION. <Y> IS A STRING OF FORM 'A FNNAME'
[2] ⍝ THE GLOBAL <∆INTERR> SPECIFIES ACCURACY
[3] ⍝ RESULT IS INTEGRAL OVER <N> SUBINTERVALS OF X[1],X[2]
[4] xCHECK:→((1≥⍴⍴X)∧(3≥⍴,X))/xOPTIONS
[5] xMESS:'X SHOULD BE VECTOR OF SHAPE 3. X[3] IS NUMBER OF '
[6] 'INTERVALS INTO WHICH (X[1],X[2]) DIVIDED'
[7] 'DEFAULT X[3] IS 1 IF NOT SPECIFIED'
[8] →0,0⍴⎕←'IF ONLY SCALAR X GIVEN THEN IT IS TAKEN AS X←0,X,1'
[9] xOPTIONS:→(1<⍴,X)/tWO
[10] X←0,X
[11] tWO:→(3=⍴X)/xCHECK2
[12] X←X,1
[13] xCHECK2:→(X[3]≠1⌈⌊X[3])/xMESS
[14] M←1
[15] R←(N←X[3])⍴0
[16] RANGE←X[2]-X[1]
[17] lOOP:RT←R
[18] M←M×2
[19] DX←RANGE÷S←M×N
[20] GRID←X[1]+DX×0,⍳S
[21] FO←⍎Y,' GRID'
[22] GRID←X[1]+DX×(⍳S)-0.5
[23] FE←⍎Y,' GRID'
[24] R←(DX÷6)×+/(N,M)⍴(4×FE)+(1↓FO)+¯1↓FO
[25] →(∆INTERR<⌈/|R-RT)/lOOP
∇
Appendix 3: Valid symbols in keyword APL¶
Apart from alphabetics and digits, the only ASCII symbols which have a meaning in keyword APL are as follows:
ASCII APL equivalent
------------------------
! ⍝
) )
> ]
/ ÷
' '
+ +
. .
( (
< [
; ;
* ×
: :
Hash ⎕
- - or ¯ according to context
& ⋄
_ ∆ (NB Valid in variable names - eg JUNE_DATA)
Editor: A review of QL/APL appears in the Technical Section of this issue of VECTOR.