K-Lite: First Impressions
Vector 15:1, page 36
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 36
This is the first instalment of a review of K-lite, a time-limited, reduced version of K, a language and programming environment with some of its roots in APL. A few days ago I downloaded my own copy of K-lite and its associated documentation — the K User Manual, K Reference Manual, and K Reference Card — from KX Systems’ website at www.kx.com. I’ve now read through both manuals and begun experimenting with K-lite.
Perhaps this instalment should really be called “Second Impressions”, since several of my first impressions of features in K have already changed in just those few days. But for that very reason I think it’s important that I publicize my thoughts at this stage, so that you the reader might learn something from following the progress of my own developing understanding. My next instalment — a more comprehensive and detailed review — is scheduled for the next issue of Vector.
What is K?¶
The first thing I should say is that K is not APL. J may be a dialect or variant form of APL, or it might be called “son of APL”, but the relationship is no more distant than that. K, on the other hand, is more like a second cousin. Or maybe I should say a great grandchild, since APL is clearly one of K’s ancestors, but a majority of K’s characteristics just as clearly display other ancestry. K’s principal architect is Arthur Whitney, known for his previous creations, the language A, and its successor A+. When I was working with A I tended to characterize it as being composed of “one third APL, one third C, and one third Arthur.” I’m inclined to describe K in a similar fashion as “20% APL, 20% LISP, and 60% Arthur.”1
I will say that K’s design, with several similarities to APL and numerous differences, seems to be at least as well thought out and self consistent. Some of its features might be profitably borrowed by APL; others represent a different but equally valid approach to problem solution, and there are some I’m not yet sure about.
Admiration and Frustration!¶
After reading through the User Manual and the Reference Manual, I was quite impressed by some of the features. I attempted some simple expressions and my admiration grew. While there are a few details I might question, on the whole I’d say that this is one very well designed language!
Then the frustration began as I tried to build the sample application as described in Chapter 3 of the User Manual. The frustration was twofold: first, the interactive “console” lacked the simplest editing and scrolling. Secondly, the features which are not available in K-lite include some which are needed by the sample application, so it can’t be made to work.
Editing, the Session, and the Console¶
In my console, the cursor keys don’t work, nor does the mouse interact with the console, so insertion isn’t possible, and deletion is only through backspace. Lines that scroll off the screen are lost; they can no longer be viewed, copied or edited. Luckily, I had also enrolled in the K listserv, so I asked there for help and information, and I got some.
I discovered that the default console under Windows 95 is basically a DOS session with reduced editing capabilities. I am told that the Windows NT console does have some of the standard editing and scrolling capabilities which I found so frustratingly absent in Windows 95. In either case, a semi-interactive double-window approach seems a bearable substitute: In this approach, all user input is though an editor in one window, while execution is done in a separate window, by either copying or loading.
An alternative approach — and the one apparently used by most current K users — is to use emacs for both editing and execution. Members of the K listserv told me where I could get a free copy of emacs from the net, which I have now downloaded. I know it’s quite powerful, but now I have to learn to use it.
Lost connections¶
The documentation includes the following disclaimer with regard to K-lite: “K-Lite is a time-limited, reduced version of K which enables interested developers to learn the language and develop small applications. K-Lite consists of the K language and interpreter, GUI software, and ASCII file read/write capability. It does not include connections, file mapping, interprocess communications or runtime capabilities.”
Unfortunately, the features not included are not only among those emphasized in KX Systems’ promotional literature, they also include some needed to implement the one significant programming example given in the documentation. This reduces K-lite’s value in introducing K to newcomers.
It’s not that K without these features is useless, but it would have been nice to have some non-trivial exercises that work, examples that demonstrate K techniques by doing real work without needing the missing functionality. I’m trying to design some small applications myself — as a means of learning the strengths and limitations of K — but I can’t be sure that I’m doing things in a reasonable way. How do I know I’m not like a Fortran programmer happily writing loops in APL because he doesn’t know any better?
And an introductory tutorial with examples that work in the K-lite environment becomes all the more important when one basically has only one chance to try it. K-lite comes with a 60-day time limit and no offer for extending that limit, yet the rumoured price tag2 for full-featured K would put it completely out of the reach of any ordinary person.
Still, don’t be put off¶
K embodies some powerful new concepts. Using K-lite to learn about them should provide new perspectives in problem solving. Sixty days should be long enough to get you started with that. In addition, you might find that some features could be compatibly added to existing APLs, and you might want to lobby for one or more of them to be added to your favourite implementation. In order to do that you’ll first have to discover them. K-lite provides a way.
Let’s look at some of those concepts….
Functions, Operators, and Syntax: Pay Attention, Now¶
Starting out simply, K can look very much like APL, e.g.
The same expression in APL gives the same result. The five functions (verbs) and one operator (adverb) seem to have the same meanings; the syntax appears to be the same, and the “atomic” functions seem to apply element-by-element to entire arrays (Vectors, at least. Actually, in K they’re lists, but I’ll get to that in a minute.), like their scalar-function APL counterparts.
There are other similarities. All K primitive functions are single characters, though the characters are all straight ASCII. There are also system functions and variables, with names starting with a special character (underbar in K, quad in APL). But some functions which are primitives in APL are system functions in K, many familiar APL symbols represent different functions in K, and K even has some new functions and operators. One of these is “Where” (monadic &). With a boolean argument &b gives the same result as b/⍳⍴b in APL, but without generating the full ⍳⍴b as an intermediate step.
Enter brackets, stage right¶
These are small differences, of the sort one might expect with a minor variant of APL. But what about the following?
This is the same computation as above, but in two places it uses an alternative bracket-based syntax for specifying function arguments.
Actually, I think list (bracket-semicolon) form is the primary syntax, while infix (as in 3+4) is the alternate. Infix notation is convenient for dyadic functions, but the list notation can be extended to any number of arguments… three, seven, or even none. In fact, this provides a way of distinguishing between display and execution of a niladic function:
The first line defines the niladic function foo.3 On the second line, using the function name without any arguments passes on its definition for assignment, or (in this case) display. The third input (indented) line has an argument list of length zero, so it executes, returning the result of seven.
Data: Remove Your Blinders!¶
Data. This is where K is really different from APL. Our old concepts don’t apply. Some details seem at first to be steps backward: integers and floats are not identical, though they can be compared. And K doesn’t have multi-dimensional arrays, but only lists. Well, don’t let your prejudices fool you!
Lists¶
I haven’t yet made up my mind about the integer/float distinction, but K’s lists seem every bit as powerful as APL’s arrays. Lists can be homogeneous or heterogeneous, and they can be arbitrarily nested. In fact, the way that nesting is handled by K’s primitive functions makes regular nested lists equivalent to arrays. Not just similar; not just a simulation; but functionally identical, e.g. indexing4 a nested list:
Not only is this the standard syntax for indexing a matrix, but the “rectangular” second selection is displayed two-dimensionally. There are also primitive or system functions for operations such as transpose and least squares (matrix divide). I think this point is significant enough that I’ll repeat it: regular nested lists are higher-dimensional arrays. It’s not that they can be interconverted, rather that they’re two different interpretations of the same structure.
Namespaces, Directories, and More¶
In K all namespaces form a single tree. Objects can be referenced and even created5 using the standard syntax with dots as separators:6
one.one.a: 5 7 9 11
one.two.a: 2*!4 / 2×⍳4
one.two.plus: +
one.two.a[1 3]
2 6
one[`one `two;`a;1 3]
(7 11
2 6)
The first two input lines above create variables in subdirectories. The third creates a function. The fourth is simple indexing. But what’s that fifth input line doing? In K one can select named objects by using their names as indices just as one can select ordered objects by using their relative locations. That line selects elements 1 and 3 from each object a in each of the two namespaces one and two in the namespace one, and the result is a two-element list, each element of which contains two elements, i.e., a matrix. Indexed assignment can be done in the same way. This is powerful!
You must have noticed something else there; the named objects are indexed using a new data type, the symbol. These are names in a form that can be thought of as pointers or handles, but they are distinct from character strings. Symbols can also be created independently of named objects, and in fact symbols can be created which don’t represent valid names, but you can see that just their use as name referents is a significant concept. I think it’s one APL would do well to borrow.
And there you see another important concept. Functions are values, and those values can be elements of lists in the same way as numbers, characters, symbols or other lists. These values can be assigned, indexed, displayed, or even applied.
In this context the dot is the function “apply”, which would seem to blur the distinction between functions and operators just as allowing functions as items of lists blurs the distinction between data and functions.
We can create function lists and apply them to lists of data using the each operator (single quote); we can even combine this with the pervasive nature of atomic functions:
Attributes!¶
Attributes are nothing new, but K has significantly extended the concept. Every object has a syntactic class; all lists have length and depth; APL arrays have rank and shape; every variable has a type and a value; functions have valence. Setting and reading these attributes is generally done by special functions or syntax even in K, but K has generalized the concept of attributes, with a general syntax for setting and accessing their values. In addition, some of them are active, not passive; they cause things to happen whenever their associated objects are either modified or referenced.
The simple syntax for attributes uses double dots, e.g., a..d is the display attribute for the object a. A number of single-letter attribute-names have reserved interpretations, but in general the K user can assign arbitrary values to arbitrarily-named attributes. These can then be employed however the user wishes.
But user-defined attributes are all passive; their contents must be explicitly retrieved to be useful. On the other hand, reserved attributes can be active, and three appear particularly important. These are dependency (..d), trigger (..t), and class (..c).
Dependency allows the user to provide an expression that will dynamically update the value of its associated variable whenever that variable is accessed and one of the named objects in the dependency has been updated. In a sense, this is like a formula in a spreadsheet cell. Circular dependencies are not allowed; they are detected and reported as loop errors.
A trigger is an expression that is executed immediately after the value of its associated object changes. A dependency makes internal changes based on external events, but a trigger performs external actions based on internal events. Thus, the following two expressions would seem to be equivalent, though there may be subtle differences I’m not yet aware of:
And the latter may be rewritten using the system variable for self reference:
The class attribute specifies the GUI display class for its associated object. Possibilities include `form, `data, `chart, `plot, `button, `check, and `radio. This attribute determines the how the object will appear when displayed as a GUI object. Which brings us to the next major concept, K’s GUI. It’s like nothing else I’ve seen.
Graphical User Interfacelets?¶
The standard GUI concept is of a single, master Graphical User Interface to which you send requests about what sorts of display areas — windows, buttons, graphs — you want and which data you want pasted into each one. K turns this inside out. Each object carries its own personal display information in the form of attributes. This includes not only the display class mentioned above, but such properties as size, colour, font, label, and action on mouse click. It should even be possible to convert the display of a numeric list from a column to a graph just by resetting its ..k attribute7.
If the “editable” attribute (..e) on an object is set, then the display can be edited, and any changes will be reflected in the value of the actual object in the workspace, and in any objects affected by associated triggers or dependencies.
The only property that isn’t handled in this way is visibility, i.e., whether to show or hide the display defined by the object’s attributes and value. Instead, this is manipulated by two special left arguments to the format primitive ($), `show and `hide. Perhaps I’m missing a subtle reason for this, but at the moment it seems to me that this property could and should also be handled as an attribute.
More Notation: Semicolons, Brackets, and …¶
Some have maintained that bracket-semicolon notation in APL is anomalous and should be removed from the language. A few have suggested the opposite, that the notation is a good one and should in fact be extended. Until now I’ve been a member of the former group, and the proposals for extensions have seemed to offer no improvement. Has K finally done it right?
A major problem with bracket-semicolon notation in APL is that a result can be produced which can’t be assigned or re-used. [a;b;c] is not assignable; nor is a;b;c, and it’s not possible to create a variable x which can be used in the expression a[x] where a has any dimension other than one. With this notation, rank has to be hard-coded into the program.8
J’s solution to this dilemma eliminated the brackets and upgraded semicolons from syntactic separators to functions. Then the special case of elided indices (e.g., a[x;;y]) was simulated by another convention, involving an additional level of nesting.
In K, on the other hand, brackets (“square” brackets), braces (“curly” brackets) and parentheses are all intimately connected with semicolons in list notation. Brackets and braces signify special interpretation of their contents (indexing and function application for brackets; function definition for braces), but any result created within this notation can be assigned, modified, and re-used. And an elided list element has a “value” of _n, or “not a value”, e.g., (;;) is identical to _n,_n,_n.
Actually, like infix notation, brackets seem to be handy but redundant. If one defines
then x g (list) would be the same as x[list]. In fact, this g is the primitive dot (.):
Redundancy aside, K appears to provide a consistent, functional, powerful and complete notation using semicolons and the various kinds of brackets.
Conditionals and control structures¶
I’ve never felt that traditional control structures fit well into APL. After all, APL already has constructs that implement particular iterations in specific contexts. Aside from itemwise (or pervasive) application of functions, these are certain operators: inner and outer product, reduce, scan, each, and (in Sharp APL) rank. I always felt that it would be more appropriate if APL also implemented IF, DO, WHILE, etc. as operators. The only trouble is that APL doesn’t have a syntax for expressions as arguments or operands. Well, K does.
Expressions and functions in K are valid list elements, just like numbers, characters, symbols and other lists. Thus it’s only natural for K to implement controlled blocks as argument lists. if[condition; expression] doesn’t need an endif; in fact, it would be silly. And expression can be more than one expression; alternatively it could be a list of expressions.
Semicolons and newlines¶
An independent feature of semicolons in K is that outside quotes and in incomplete expressions, they are identical to new lines, e.g.
This feature may not seem too important in function definition, though it can be used to improve the readability of code. More significant to my mind is that the same equivalence holds true for data input. This means that a matrix can be entered in the same intuitive multi-row format as its display:
Miscellaneous¶
K has many other features, including error trapping and other debugging tools, various kinds of file I/O, interfaces to other processes, and much more. I don’t have the time or space to report everything I know here, and there’s still a lot I don’t know. The interfaces will be hard to learn, since most are not included in K-lite.
K is not nearly as permissive as APL in its treatment of white space. White space can have syntactic meaning, i.e., the presence or absence of white space can completely change the meaning — and therefore the result — of an expression. (E.g., +[2;5] gives the result 7, but + [2;5] gives a parse error.) At the moment I’m finding this a bit difficult, though I hope I’ll learn the rules fairly quickly.
There are several different ways to do both indexing and function application in K. Perhaps with practice I’ll come to see the unique advantage of each function and syntax, but at the moment it just seems redundant and confusing.
In my experimentation so far I’ve run across some details that don’t work as I expected. Are they inconsistencies, or just misinterpretation on my part? Hopefully, I can tell you in the next issue.
Next issue:¶
I look forward to presenting a more detailed and experienced view of K — or at least K-lite — in the next issue of Vector. In the meantime you might want to download your own trial copy from KX Systems’ web site.
-
My knowledge of LISP is quite limited, and I know almost nothing about JAVA, DELPHI, and many other popular — and less popular — languages. For this reason, if any readers note similarities that I have missed between K and other languages, please let me know via e-mail (jel@danbbs.dk) or a letter to Vector, and I’ll try to include that information in my next instalment. ↩
-
I haven’t yet confirmed the rumour with KX Systems, so I won’t quote it here. I will try to get firm price information for the next instalment of this review. ↩
-
I won’t try to explain the details of that syntax just yet, except to say that the colon following a name is K’s equivalent of the assignment arrow. The same symbol is used for assignment of both variables and functions. ↩
-
K’s index origin is always zero. ↩
-
By now you should have noticed that K uses the colon like an assignment arrow. ↩
-
Here are a few symbols which are different from their APL equivalents: star for multiplication, exclamation point for index generation, and slash for comment. ↩
-
Actually, I’ve tried this and failed, but maybe I’ve just misunderstood something simple. Or is graphing one of the features unavailable in K-lite? ↩
-
Or constructed as text and executed. Ugh! ↩