Elias Mårtenson

@loke@functional cafe

It appears a lot of my inspiration for writing blog posts comes from me listening to podcasts. This time I was listening to A Problem Squared with Bec Hill and Matt Parker. The episode in question was Episode 103 – Heaps Splad and Paul’s Pad.

I strongly recommend listening to the episode (in fact, all of the episodes) because it's really good. But, in case you don't have time to listen to it right now, please allow me to summarise one of the problems being solved:

Are there any words which, when encrypted using a Caesar cipher, yields another valid word?

Matt Parker explained that he iterated over all the words using some (in his words, “terrible”) Python code. Whether the code actually was terrible or not will remain unanswered (the code is likely not bad, since if you are good enough to know your code is bad, you're probably better than average), but regardless of the quality of the code, I think we can write something much more concise. We can make this assumption simply by virtue of the fact that Python was used.

We'll obviously use Kap for this, because why not?

Getting the word list and cleaning it up

I don't know which word list Matt Parker uses, but I found this one on Github which contains 466,550 words, which is in the same ballpark as the one mentioned in the podcast.

Let's load it into a variable, and print the size:

    words ← io:read "words.txt"
    ≢words
466550

The list contains a lot of words with punctuation, numbers, etc. There are also upper case letters in it that we want to turn into lowercase. This is how we fix this:

words ← ∪ ("^[a-z]+$" regex:match)¨⍛/ unicode:toLower¨ words

The entire list is passed through the unique () function to remove any duplicates due to upper and lower case versions of the same word existing in the list.

After doing this cleanup, we're left with 416,292 words.

Computing the ciphered versions of a word

Our goal is to create a function which accepts a string, and returns the 25 different rotated versions of that word (26 letters in the English alphabet, but we are not interested in the 0-rotated version).

To do this, we will compute the index of each letter (by subtracting @a), add the offset, take the result mod 26 and convert back to a letter (by adding @a). Let's write a function called scramble that does this:

scramble ⇐ { @a + 26| ⍺ + ⍵-@a }

Let's try it:

    1 2 3 4 scramble¨ ⊂"abcd"
┌→──────────────────────────┐
│"bcde" "cdef" "defg" "efgh"│
└───────────────────────────┘

Finding all the words

Since we're going to do a lot of word matches, we'll start by putting all the words in a hashmap. Let's call it wordsMap:

wordsMap ← map (⍪words),1

Next, we'll compute a 2-dimensional array where each row represents a word, and each column the n-scramble of that word. We can do that with an outer product with the words on the left and the numbers 0 to 25 on the right (represented using ⍳26):

allScrambledWords ← words scramble⍨⌻ ⍳26

Since this performs the computation on every word this will take a while (about 10 seconds on my workstation, which is reasonably fast) so don't worry if you don't get a result right away.

Now we want to find which of these scrambled words are real words, which means we need to look them up in the hashmap we created earlier.

When using bracket indexing to look up a value in a hashmap, the return value is null if the key does not exist in the hashmap. So, we'll simply do a not-equals comparison against null to get a boolean array that indicates the locations where we found a word. We'll then assign this boolean array to the variable result:

result ← null ≢¨ wordsMap[allScrambledWords]

Analysing the results

Now comes the fun part. We now have all the data we need, so now we can start answering questions about this data.

How many words scramble into at least one word?

To solve this, we drop the left-most column (since that column represents the 0-scramble, or the word itself) and perform an OR-reduce over the rows and sum the result:

    +/ ∨/ 0 1↓ result
10850

Create a table of the results

Now we know the resulting list is 1,0850 elements long. We can

For each row in result and allScrambledWords, we'll pick the values where the row in result is 1, and then filter out any rows which only have a single entry (i.e. it doesn't scramble into another valid word):

groups ← (1<≢¨)⍛/ result ((⊂(/))⍤1) allScrambledWords

We can confirm that the resulting list is 1,0850 elements long:

    ≢groups
10850

We'll also note that if a word scrambles into N different other words, then the same set of words will be repeated N times in this list. We'll eliminate these duplicates by sorting each sublist and calling the function on the result, then assigning that result back to the groups variable:

groups ← ∪ ∧¨ groups

What is the longest word in the result?

To find this, we'll just sort by the length of the first word:

groups[⍒(≢↑)¨groups]

The first result is “abjurer”/“nowhere”, which is 7 letters. It's a bit disappointing that there isn't anything longer, but I was prepared for this after listening to the podcast.

Another interesting results are “unfiber”/“bumpily”, but to be honest, most of the 7-letter results are boring.

Which is the word with the most matches?

To find this, we just sort by the length of each element:

groups[⍒≢¨groups]

This gives us a very uninteresting list, since the top results are all the one and two letter words, because for some reason the word list contains all single letters and almost every combination of two letters, so we'll have to filter them out:

((2<≢↑)¨)⍛/ groups[⍒≢¨groups]

The three letter results are also not interesting at all. Apparently ajc is a word, which scrambles into gpi, mvo, yha, etc.

Four letters is more interesting. We'll display the first 8 rows:

    8 ↑ ⍪ ((3<≢↑)¨)⍛/ groups[⍒≢¨groups]
┌→─────────────────────────────────────────────────┐
↓┌→───────────────────────────────────────────────┐│
││"anan" "bobo" "erer" "lyly" "nana" "pcpc" "vivi"││
│└────────────────────────────────────────────────┘│
│              ┌→─────────────────────────────────┐│
│              │"adad" "bebe" "fifi" "lolo" "ruru"││
│              └──────────────────────────────────┘│
│              ┌→─────────────────────────────────┐│
│              │"afcc" "chee" "diff" "joll" "purr"││
│              └──────────────────────────────────┘│
│              ┌→─────────────────────────────────┐│
│              │"cece" "gigi" "momo" "susu" "yaya"││
│              └──────────────────────────────────┘│
│                     ┌→──────────────────────────┐│
│                     │"abib" "dele" "novo" "stat"││
│                    └───────────────────────────┘│
│                     ┌→──────────────────────────┐│
│                     │"actg" "cevi" "priv" "yare"││
│                     └───────────────────────────┘│
│                     ┌→──────────────────────────┐│
│                     │"adds" "beet" "illa" "lood"││
│                     └───────────────────────────┘│
│                     ┌→──────────────────────────┐│
│                     │"admi" "benj" "firn" "svea"││
│                     └───────────────────────────┘│
└──────────────────────────────────────────────────┘ 

What are the takeaways from all of this?

Some readers may walk away from this wondering why one should bother with this complicated syntax that uses symbols instead of words.

But the key point here is how we could solve this problem without actually writing any code. In a way that is not too dissimilar to how a good Unix user can solve almost anything on the commandline without writing a program, the array languages (and not just Kap) are all very good at letting you get to the result without actually doing any “programming”.

Whether or not it's worth the effort to learn the syntax in order to be able to leverage the power of these tools is up to you. I personally believe it's worth it.

Last weekend I was listening to episode 99 of Array Cast. The topic of the fortnight was array indexing, or the way to read values out of arrays.

A programmer unfamiliar with array languages may wonder how such a simple concept can fill an entire hour-and-a-half episode (and they may even have to extend it to another episode), and to answer that question I would recommend listening to the episode, but the short answer is that array languages focus on organising data in arrays and then performing actions on entire arrays in one operation. This means that reading and writing data to/from arrays is probably the most important operation one does when using an array language.

In the episode, Stephen Taylor talks about how Q allows for indexing the columns of a table by name rather than a number. This is a nice feature that it shares with R.

However, Kap also has this functionality. It's really not well documented at all and I'll try to improve the documentation, but the hope is that this blog post will serve as an introduction to this feature.

Creating labelled arrays

In Kap, arrays can carry some metadata that describes its content. This metadata includes the axis labels, which are updated or accessed using the function labels (docs).

Here's how you can assign labels to the columns of an array:

    content ← 4 3 ⍴ ⍳12
┌→──────┐
↓0  1  2│
│3  4  5│
│6  7  8│
│9 10 11│
└───────┘
    content ← "foo" "bar" "abc" labels content
┌───┬───┬───┐
│foo│bar│abc│
├→──┴───┴───┤
↓  0   1   2│
│  3   4   5│
│  6   7   8│
│  9  10  11│
└───────────┘

The labels is additional metadata and does not change the shape nor content of the array:

    ⍴ content
┌→──┐
│4 3│
└───┘

The labels function accepts an array of strings on the left, and assigns them to the given axis. The number of elements have to match the size of the given axis. By default this function assigns labels to the last axis, but an axis argument can be used to specify which axis to use:

    content ← "Satu" "Dua" "Tiga" "Empat" labels[0] content
┌───┬───┬───┐
│foo│bar│abc│
├→──┴───┴───┤
↓  0   1   2│
│  3   4   5│
│  6   7   8│
│  9  10  11│
└───────────┘

The above call assigned labels to each row, but it's not displayed. The labels are there, but the current version of the text renderer does not include them. However, we can transpose the array to show that they are there:

    ⍉ content
┌────┬───┬────┬─────┐
│Satu│Dua│Tiga│Empat│
├→───┴───┴────┴─────┤
↓   0   3    6     9│
│   1   4    7    10│
│   2   5    8    11│
└───────────────────┘

Calling labels monadically will return the labels for a given axis:

    labels content
┌→────────────────┐
│"foo" "bar" "abc"│
└─────────────────┘

Using labels

Some functions returns arrays with labels where appropriate. The main one being the sql:query (reference) function which adds the column names as labels.

When reading CSV data, the first row is often a set of labels:

    csv ← io:readCsv "file.csv"
┌→──────────────────────────┐
↓"col1" "col2" "col3" "col4"│
│     0      1      2      3│
│     4      5      6      7│
│     8      9     10     11│
│    12     13     14     15│
│    16     17     18     19│
└───────────────────────────┘

When loading a CSV, instead of just dropping the first row, it can be assigned as column labels:

    csv ← (>1↑)«labels»↓ csv
┌────┬────┬────┬────┐
│col1│col2│col3│col4│
├→───┴────┴────┴────┤
↓   0    1    2    3│
│   4    5    6    7│
│   8    9   10   11│
│  12   13   14   15│
│  16   17   18   19│
└───────────────────┘

Selection by label

The Kap standard library contains the s namespace, which contains a few utility functions which are not documented yet, since they are still a work in progress.

One of these functions is the s:cols function, which returns columns from a table based on the column labels:

    "col3" "col4" s:cols csv
┌────┬────┐
│col3│col4│
├→───┴────┤
↓   2    3│
│   6    7│
│  10   11│
│  14   15│
│  18   19│
└─────────┘

There is also the s:col function if you only want to read one column:

    "col2" s:col csv
┌→──────────┐
│1 5 9 13 17│
└───────────┘

Chart functions

If present, axis labels will automatically be used as labels in charts:

temperatures ← `
        "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" labels "Singapore" "London" labels[0] 2 5 ⍴ 31 29 30 31 31 5 7 10 13 13
chart:line temperatures

This will display the following chart:

Labelled axis in chart

This is a short update to my previous post where I will solve the second part. At the end, I will also include one-line solutions to the two problems, which really doesn't involve more than removal of unnecessary variable assignments.

Revisiting part 1, here's the full solution that was written in that post (with a few less variable reassignments):

numbers ← ⍎¨ ⊃ {(@\s≠⍵) ⊂ ⍵}¨ io:read "part01.txt"
(a b) ← ⊂[0] numbers
a ← ∧a
b ← ∧b
+/ | a - b

Solution to part 2

Let's keep the first two lines from the solution to part 1. Here we have the two arrays in a and b.

The problem statement explains that for each number in the left column (array a), we want to multiply that value with the number of times that number occurs in the right column (array b).

This begs the question: How do we count the number of occurrences of a value in an array?

Let's say we have the following array: 1 2 10 2 2 1 5, and we want to count the number of 2's.

We take advantage of the fact that the values for false and true in Kap is 0 and 1 respectively. So all we need to do is to compare the array with the value we're looking for:

    2 = 1 2 10 2 2 1 5
┌→────────────┐
│0 1 0 1 1 0 0│
└─────────────┘

And then we can take the sum of the result:

    +/ 2 = 1 2 10 2 2 1 5
3

We can now write a function that takes the value to search for on the left, and the full array of values to search for on the right:

{⍺ × +/⍺=⍵}

Since the left and right arguments are called and respectively, this multiplies the left argument with the number of occurrences of that value in the right argument.

All we have to do now is to call this function once for each value in a, passing in the entire argument b to the function, and then sum the result:

+/ a {⍺×+/⍺=⍵}¨ ⊂b

We use to enclose the array b into a scalar in order to ensure that the entire array is passed to each invocation, instead of mapping over all elements in the array.

The final code looks like this:

numbers ← ⍎¨ ⊃ {(@\s≠⍵) ⊂ ⍵}¨ io:read "part01.txt"
(a b) ← ⊂[0] numbers
+/ a {⍺×+/⍺=⍵}¨ ⊂b

One-line solutions

One-line solution to part 1

We can make the code a bit less verbose by not breaking out the two arrays into variables and instead keep it as a 2-element list. And once we did that, there is no need for the variable numbers, which turns the solution into a single line:

+/ | ⊃-/ ∧¨ ⊂[0] ⍎¨ ⊃ {(@\s≠⍵) ⊂ ⍵}¨ io:read "part01.txt"

One-line solution to part 2

There are shorter solutions to this that I was considering including here, but they take advantage of more advanced concepts, so instead I show this solution, which should be understandable without knowing how compositional operators work.

We're simply creating an outer function to automatically bind the two arrays as function arguments instead of explicit variable assignment.

{ +/ ⍺ {⍺×+/⍺=⍵}¨ ⊂⍵ }/ ⊂[0] ⍎¨ ⊃ {(@\s≠⍵) ⊂ ⍵}¨ io:read "part01.txt"

So Advent of Code is done, and I made an actual effort this time. Of course I used Kap, and the final tally ended up being 32 out of 50 stars. I may solve some more later if I feel bored.

The Advent of Code problems tend to be very good fits for array languages, and array language solutions are often much shorter than solutions in other languages. In particular, the parsing which is often annoying in many languages often reduce down to a few characters in Kap.

Let's take a look at day 1 and solve it in Kap. I will attempt to describe the steps like one would solve it interactively in the REPL, rather than show a program and explaining how it works.

In practice, most of the you use Kap to solve problems, it is used as an interactive calculator. Compared to most other languages, the point at which one has to leave the REPL and open an editor instead is much later. Many of the early Advent of Code problems can be solved this way, for example. Day 1 is certainly one such example.

The description for day 1 basically boils down to the following problem:

Given a text file containing a 2-column list of numbers, match up the smallest number in each column and take the difference between them, then do the same with the second-smallest pair and so on. Finally sum up all the differences.

What this means is that we want to do the following:

  • Parse the input file into two lists of numbers
  • Sort each list individually
  • For each pair of numbers, subtract one from the other and take the absolute value
  • Sum the resulting list

Let's write the Kap code to do each of these steps:

Parse in the input file

We'll store the input in a file called part01.txt, which has the following form. I'll include 3 lines of input in order to make the examples small, but the actual input data contains 1000 pairs of numbers.

3   4
15  3
2   10

Kap provides a handy function called io:read which accepts a filename and returns a list of strings, where each string is one line from the input file. Thus, we can type the following:

    io:read "part01.txt"
┌→────────────────────────┐
│"3   4" "15   3" "2   10"│
└─────────────────────────┘

For each line, we want to split the string on spaces. This can be done using a Regex, but since this is such a simple split, we'll use a more traditional approach.

The function takes a bitmap on the left, and an array on the right, and returns a new array where the elements are grouped based on the values on the right (the function can do more than that, but for example, this is all we need). Here's an example:

    1 1 0 1 1 ⊂ "abcde"
┌→────────┐
│"ab" "de"│
└─────────┘

To use this, all we need to do is to somehow create a bitmap that contains 0 wherever there is a space, and 1 otherwise. This is achieved by comparing the entire string with a space. The space character is represented using @\s, and we'll use the not equals function to compare each character in the string with a space. We can then put all of this together in a dfn. This is nothing more than a function where the right argument is assigned to the variable , and the left . In the below example, we don't pass a left argument, so only is used:

    {(@\s≠⍵) ⊂ ⍵} "this is a test string"
┌→──────────────────────────────┐
│"this" "is" "a" "test" "string"│
└───────────────────────────────┘

Since we have an array of strings that we read from the file, we want to call this function once per element, which is achieved using the for each operator: ¨.

    rows ← {(@\s≠⍵) ⊂ ⍵}¨ io:read "part01.txt"
┌→──────────────────────────────┐
│┌→──────┐ ┌→───────┐ ┌→───────┐│
││"3" "4"│ │"15" "3"│ │"2" "10"││
│└───────┘ └────────┘ └────────┘│
└───────────────────────────────┘

We'll also convert the list of pairs into a 2-dimensional array, using (documentation):

    rows ← ⊃rows
rows ← ⊃rows
┌→────────┐
↓ "3"  "4"│
│"15"  "3"│
│ "2" "10"│
└─────────┘

We also want to convert each string to a number, which is done using the parse number function: . We'll call this function on each element:

    numbers ← ⍎¨ rows
┌→────┐
↓ 3  4│
│15  3│
│ 2 10│
└─────┘

This code can of course be put together in a single line without having to store the intermediate results in variables. This is left as an exercise for the reader (or just look at the Codeberg repository).

Sorting the lists

There are many ways in which this can be solved, but this example will use a very straightforward approach by extracting the columns into individual variables and work with them independently. This works because the problem only has two columns. If there were more columns, this would of course not be practical.

To extract values from a given dimension, we can use the monadic version of . The monadic version is very different from the dyadic case that was used above. We'll use the axis form, which allows you to specify the axis by which the values should be extracted. In this case, we want to extract the values across the rows, meaning the first axis (axis 0). Thus, we'll type the following to obtain the two columns and assigned them to two variables, a and b:

    (a b) ← ⊂[0] numbers
┌→────────────────┐
│┌→─────┐ ┌→─────┐│
││3 15 2│ │4 3 10││
│└──────┘ └──────┘│
└─────────────────┘

a now contains 3 15 2 and b contains 4 3 10. But we also want to sort them, so let's do that:

a ← ∧a
b ← ∧b

Compute the differences

Now that we have the two sorted lists in a pair of variables, computing the differences is very simple. All we have to do is to subtract one from the other:

    a - b
┌→──────┐
│-1 -1 5│
└───────┘

We also want to take the absolute value of these numbers, which is achieved using the magnitude function: |:

    | a - b
┌→────┐
│1 1 5│
└─────┘

Sum the resulting values

Taking the sum of an array in Kap (or most other array languages) is one of those things that is used to demonstrate the flexibility of the language, and how you can leverage operators to express complex operations with very key keypresses, which is very useful when working interactively like we're doing here. In this case, we perform a reduction over the add function on the numbers in the list:

    +/ | a - b
7

And there you have it! The solution to the first part. The second part is a small extension to the first one, which we'll solve later.

You may have heard about how array programming languages such as APL, J or K. If you have, you've probably heard that code written in these languages is incredibly dense and unreadable. “Line noise” is a term often used to refer to them.

In this post, I will try to use Kap to give an introduction to the language by using an imperative programming style. Actual Kap code is a mix of terse and verbose styles, but perhaps illustrating the verbose style first provide a different perspective.

If you want to get an introduction to the terse style immediately, you can read the Kap tutorial. Most APL tutorials will also be useful, although there are some differences between Kap and APL (explained here).

This post assumes that the reader has familiarity with various imperative programming languages, such as C, Java or Javascript.

The goal is explain the basic syntax of Kap in a way that is as similar to these programming languages, and then show how adding a little bit of line noise can make expressions much more concise.

Hello world

Let's start with Hello world. It's very simple:

io:println⟦"Hello world"⟧

The double-struck bracket is used when calling a function in an imperative style. This is the same as the parentheses around the arguments to a function in C.

Note: In this particular case, the brackets are optional, but we're getting ahead of ourselves.

While this post is trying to introduce the language in a way so as to make it easier for people without array language familiarity, one still has to accept the use of non-ASCII symbols. If this is something you really cannot get over, then K or Q are probably the languages that would be most suited to your liking.

In any case, I don't think much explanation is needed. Perhaps just worth mentioning that io is the namespace and println is the name of the function.

Assign to a variable

Here's how you assign a value to a variable:

message ← "Hello"
io:println⟦"The message is: ",message⟧

The , is not an argument separator. It's actually a function that concatenates two arrays (strings are just arrays of characters). The resulting string is then printed.

Loop with a counter

Kap features while loops just like most other languages. The syntax is very similar to these languages too:

i ← 0
while (i < 15) {
    io:println⟦"Number: ",⍕i⟧
    i ← i + 1
}

The only unexpected thing here should be the symbol . This is used to convert number to a string. This conversion isn't strictly necessary here since io:println will automatically convert numbers to strings prior to printing, but I wanted to add it for completeness.

Calling a function with multiple arguments

Just like any other language, Kap allows you to declare functions with multiple arguments. The syntax may be unusual, but it shouldn't be too complicated:

∇ makeCopies (string ; n) {
  result ← ""
  i ← 0
  while ((i←i+1) ≤ n) {
    result ← result,string
  }
  result  ⍝ The return value is the last expression in a function
}

Note how we can modify a variable while using the result. This is exactly the same as in a language like C or Java, except that we have to be explicit since there is no modifying operator. In other words, we have to write i←i+1 rather than ++i (Kap's assignment operator returns the value that was assigned, so the example expression is equivalent to pre-increment).

A ; is used to separate arguments, so we can call the function like so:

makeCopies⟦"zx"; 5⟧

Calling this function will return the string: "zxzxzxzxzx".

Arrays

The primary composite datatype in Kap is the array. An array is just a list of values (exactly like, say, Java) along with a dimensionality (like Common Lisp or Fortan, but unlike Java, Javascript and many other languages). That is to say, while arrays in most languages are indexed using a single integer value, a multidimensional array use multiple integers as an index.

Here's a 1-dimensional array which is assigned to the variable a.

a ← 100 200 300 400

This is an array of 4 elements, and no special syntax is needed to express it. Just listing values separated by whitespace creates a regular array. Compare this to JS, where you would need square brackets and delimiters to do the same thing.

The argument in favour of this syntax is that arrays are such fundamental objects in array languages that they should require an absolute minimum of syntax. (Some other array languages such as BQN disagree here, and require explicit syntax to create arrays)

The above example created a 1-dimensional array. What about higher dimensions? This is easy, but it does force us into the realm of symbols. It's a pretty mild use case though, so it's a good introduction:

b ← 2 2 ⍴ 100 200 300 400

First of all, the symbol is the Greek letter “rho”, and it's a function that accepts an argument on either side (just like - accepts an argument to the left, and the value to subtract on the right). In this case, the left argument is the array 2 2, and the right argument is the familiar 4-element list 100 200 300 400 that we saw above.

So what does do? Well, it “reshapes” the argument on the right to the dimensionas specified on the left. In this case, it creates new array which is 2 rows by 2 columns, with the values on the right. Typing this into the interpreter will show the following:

┌→──────┐
↓100 200│
│300 400│
└───────┘

Note that this is not a nested array (a 2-element array containing 2 arrays inside it). Instead, it's a single array where each element is indexed using a pair of integers. The pair 0 0 refers to the value 100, the pair 1 0 refers to 300, etc.

The observant reader may have noticed that the function specified the total size of the resulting array on the left (in other words, the product of all dimensions) and the array to be reshaped also has a size. What happens if the two don't match?

The reshape operation will take as many elements as it needs, and if there isn't enough, it will start from the beginning. This will become important later when we rewrite the earlier code in a terse way. Here's an example:

    4 2 ⍴ 100 200 300 400
┌→──────┐
↓100 200│
│300 400│
│100 200│
│300 400│
└───────┘

Visualising arrays

2-dimensional arrays are fun and all, but what can you do with them? Let's draw something on the screen. First, we'll create a simple image by generating it as a 1-dimenional array and then we'll reshape it into 2 dimensions so that it can be drawn on the screen.

imgdata ← ⍬  ⍝ This symbol represents the empty list
y ← 0
while (y < 100) {
  x ← 0
  while (x < 100) {
    ⍝ The symbol ⋆ means exponentiation. In other words, the
    ⍝ below expression uses the Pythagorean theorem to compute
    ⍝ the distance of the point from the centre of the screen.
    p ← √((x-50)⋆2)+((y-50)⋆2)
    imgdata ← imgdata,(p÷50)
    x ← x + 1
  }
  y ← y + 1
}
⍝ The resulting array is 1-dimensional, so we reshape
⍝ it into a 100-by-100 array before it can be displayed.
resized ← 100 100 ⍴ imgdata
gui:draw resized

Time for terseness

The above program works, and it's a perfectly acceptable way to write Kap code. However, it doesn't really take advantage of the power of the language. The idea of Kap is that you use it as a very powerful calculator, and in situations like that you don't want to write a 12 line program just to generate that array of numbers.

Let's imagine we're working in the REPL, and we have no intention of saving this program to a file or share it with others. I'm making this initial assumption in order to avoid having a discussion around how to document terse Kap code. That needs to be the topic of a separate post.

Index generator and scalar functions

The power of array programming come from the idea that instead of looping over some set of values, you perform operations that are defined on the entire array at a time. This includes things like mathematical operations (addition, subtraction, etc) and functional operations like mapping and reduction. Most of these core functions are a single character, which makes for a very powerful language, but is also why people tend to think it's “unreadable”.

Note: there are a lot of different ways to approach this problem, and when I was discussing this in the Array Programming Matrix Room, several different approaches were suggested. I've picked the one I will be presenting because I believe it does the best job at highlighting the topics that I'd like to discuss, not necessarily because it's the best solution.

We're going to need the index generator function, so let's start by introducing it. The name is (the Greek letter iota), and can be called with one argument, returning an array of indexes into an array of the given dimensions. For example: ⍳10 returns a 1-dimensional array containing the numbers 0 to 9:

    ⍳10
┌→──────────────────┐
│0 1 2 3 4 5 6 7 8 9│
└───────────────────┘

Note: we don't have to use the double-struck brackets to enclose the argument. When an argument is passed without these brackets, everything to the right of the function name constitute the arguments, unless override by parentheses. This means that the above could also have been written as ⍳5+5. I.e. functions are called with right precedence when called without brackets.

Next, we need to discuss scalar functions. These are functions that act on all elements in an array. For example, if we add a number to an array, this is the same as adding that number to every cell in the array. Since we're going to work ourselves up to a terse version of the previous program, let's subtract 5 from ⍳10 (we'll later use 100 and 50):

    (⍳10)-5
┌→───────────────────────┐
│-5 -4 -3 -2 -1 0 1 2 3 4│
└────────────────────────┘

Note: we use parens around ⍳10 because of the aforementioned right precedence. If we skip the parens we end up with the equivalent of ⍳(10-5).

Now, if we reshape this thing to a 10-by-10 array, we end up with an array that contains the x-coordinates of the pixels. Let's assign it to a variable because we'll need it later:

    xcoords ← 10 10 ⍴ (⍳10)-5
┌→───────────────────────┐
↓-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
│-5 -4 -3 -2 -1 0 1 2 3 4│
└────────────────────────┘

Now, if we transpose this array using the function , we get the y-coordinates, which we then assign to another variable:

    ycoords ← ⍉xcoords
┌→────────────────────────────┐
↓-5 -5 -5 -5 -5 -5 -5 -5 -5 -5│
│-4 -4 -4 -4 -4 -4 -4 -4 -4 -4│
│-3 -3 -3 -3 -3 -3 -3 -3 -3 -3│
│-2 -2 -2 -2 -2 -2 -2 -2 -2 -2│
│-1 -1 -1 -1 -1 -1 -1 -1 -1 -1│
│ 0  0  0  0  0  0  0  0  0  0│
│ 1  1  1  1  1  1  1  1  1  1│
│ 2  2  2  2  2  2  2  2  2  2│
│ 3  3  3  3  3  3  3  3  3  3│
│ 4  4  4  4  4  4  4  4  4  4│
└─────────────────────────────┘

We can square the entire array simply by using since it's a scalar function:

    ycoords⋆2
┌→────────────────────────────┐
↓25 25 25 25 25 25 25 25 25 25│
│16 16 16 16 16 16 16 16 16 16│
│ 9  9  9  9  9  9  9  9  9  9│
│ 4  4  4  4  4  4  4  4  4  4│
│ 1  1  1  1  1  1  1  1  1  1│
│ 0  0  0  0  0  0  0  0  0  0│
│ 1  1  1  1  1  1  1  1  1  1│
│ 4  4  4  4  4  4  4  4  4  4│
│ 9  9  9  9  9  9  9  9  9  9│
│16 16 16 16 16 16 16 16 16 16│
└─────────────────────────────┘

We can now put the entire thing together:

(√ (ycoords⋆2) + (xcoords⋆2)) ÷ 5

(I'm not including the output of this operation, since the array is pretty big. Suffice it to say that it does contain the numbers we're looking for)

We're not done making the program terse yet, but below is the entire program as it stands right now:

xcoords ← 100 100 ⍴ (⍳100)-50
ycoords ← ⍉xcoords
gui:draw (√(ycoords⋆2) + (xcoords⋆2)) ÷ 50

Let's compress this thing further

The program above is much closer to what you'd end up writing on the REPL, especially if it's the result of some experimentation so that you'd have these arrays in variables already. When writing it from scratch, however, you probably want to type even less, so let's use some powerful features of Kap that allows you to be even more terse.

Kap allows you to write inline functions using { and }. The code between the braces is a function in itself, with the left and right arguments being assigned to the variables and . While there are many ways in which inline functions (or dfns, as they are usually called) can be used, one use case is to provide an efficient way to evaluate some code with an expression assigned to a variable, without having to assign and name said variable separately. This is very useful in the example below, since the 100-by-100 array we created first is needed twice inside the dfn. We then inline the call to since there is no need to store that in a separate variable:

gui:draw { (√(⍵⋆2) + ((⍉⍵)⋆2)) ÷ 50 } 100 100 ⍴ (⍳100)-50

You can of course go even further. There are several repetitions in the code above, but unless we're doing code golf, there really isn't much reason to go that far. I've also intentionally avoided using a lot of features that would be quite useful here, mainly because the post is already pretty long, and such descriptions are more useful if it's part of a separate post.

It may also be of interest that the above version does not actually create several arrays of pixel data. These arrays are represented internally as abstract objects and they are not actually realised into data until the final pixmap is generated in the gui:draw function.

Terse version of the string duplication function

For completeness sake, here's one way you could write the makeCopies function above. It only uses one new function that hasn't been talked about yet: , which returns the size of its argument.

Actually, you'd probably not even put it in a function, but rather just inline it:

5 { (⍺×≢⍵) ⍴ ⍵ } "xz"

Further reading

The main Kap website is perhaps not the most beautiful website, but might contain some interesting information for people who got all the way to the end of this post.

More code examples can be found on the Kap examples page.

It has been said that the most popular programming language in the world is the spreadsheet (and in particular these days, Excel).

There is a valid argument that the spreadsheet embodies an array programming model, making Excel the most popular array programming language in the world.

As the author of an array programming language based on the ideas of APL, I see the popularity of the spreadsheet as both a problem (because it's so error-prone) and an opportunity (because while the fans of imperative languages like Python are probably too set in their ways to consider an alternative model of computation, users of spreadsheets are already thinking in terms of matrices of values, and some of those users may be more open to the idea of performing the same computation in a more reliable way).

Before discussing how to leverage the power of spreadsheets with a proper array based programming language, there is a need to discuss how spreadsheets are used today, and some of the problems with them.

The different uses of spreadsheets

When people use a spreadsheet, it's to do one of several things. In short, spreadsheets are used to perform many different activities, which can roughly be grouped into 3 main categories:

  • Data entry
  • Computation
  • Presentation

Data entry

Ever since VisiCalc was released in 1979, the spreadsheet has presented the user with a hugely inviting blank sheet of empty cells when the program was started.

Screenshot of the LibreOffice initial page
What do you want to calculate today?

The page almost calls out to the user to start inputting numbers. Our innate desire to organise things in categories is what has made this design so successful. The VisiCalc initial screen looked like this, as does the latest version of LibreOffice some 45 years later.

All you have to do is to move the cursor to the cell you want to edit, type the values and things will just work. While data entry specialist is still a job title in use today, there was a time when any data input was a complicated process that required specialist knowledge. The spreadsheet changed this to allow anyone to record anything they wanted in digital form.

Computation

Once the data is in the spreadsheet, you inevitably want to work with the numbers to produce some output.

Spreadsheets makes it very easy to take the step from simple data entry to computation of results. All you have to do is to add a =sum(a1:a5) to the sheet you'll have the sum in the cell you want.

Presentation

Spreadsheets aren't just about entering data and computing some results based on that data. It's also about presenting it in an appealing manner.

This is really where the you can see the genius of the invention. From something as simple as looking at a set of numbers, with the sum of those numbers neatly displayed as “Total:” below the values, to a complex form with coloured highlighting and different text styles to emphasise the results.

These sheets also become interactive without any extra work, since any change in the source data will automatically propagate to the sheet that contains the results.

The problems with spreadsheets as a programming environment

The spreadsheet paradigm hides what's important (the formulas) in favour of the superficial (the data)

Once you've taken that first step to compute a sum in a spreadsheet, it's not a very long road until you find yourself in one of those multi-sheet monsters most people who work in a large company have seen. I've certainly spent more time than I want to admit trying to untangle one of those bowls of spaghetti trying to find out why a certain number is computed the way it is.

Screenshot of LibreOffice showing some products, its prices, amounts and the totals

Here's a silly quiz: Can you spot the error in the above spreadsheet?

I don't think is possible to tell just by looking at the screenshot. The problem is that cell D5 has the actual value entered instead of a formula (this could have happened due to some simple copy&paste error).

And this is the true danger of spreadsheets. These kinds of mistakes are completely invisible when they happen. Instead, the results start to deviate (probably slowly) once the input data start to change, and you could be looking at invalid numbers for quite some time before anyone notices the problem. And if the dataset is large, it may be a lot of work to actually find the underlying mistake.

And this bring us to my proposition: Spreadsheets are doing a great job at data entry and presentation, but it's severely lacking as a computation tool.

However, very few tools allow you to have a seamless workflow using a proper language for computation while still using spreadsheets for the other aspects of the workflow. Once a spreadsheet has grown too large, someone decides to write a Python application to replicate it, which inevitably means that they have to build a webapp around it, which is probably going to be much less user-friendly since spreadsheets have had 40+ years of UI refinements, while the webapp was put together in an afternoon.

Spreadsheets intentionally hides errors in an attempt to do what the user thinks they want

Since this section could become very large if I listed all the examples where spreadsheets (and Excel in particular) fails to highlight problems, so here's a sample:

  • Numbers tend to be floating point, which means that the moment a value is entered, there are precision issues. This is actually something that could be improved simply by supporting rational arithmetic, although I don't think there are any spreadsheets that do.
  • If a number is entered as a string, it will look identical to an actual number (except that the alignment will be different by default). If you're lucky, you'll get an error later which will manifest as an #ERR field, and then you'll have a fun time finding the source of that issue.
  • The system invites the user to write long single-line formulas in a language that is not suited for long, single-line formulas. It all ends up being crammed into a space that is nowhere near large enough to hold it. This makes editing the code an absolutely painful experience.
  • Various errors, such as exceeding limits tend to be ignored instead of invalidating the entire document. This is something the UK government was made painfully aware of.

Using Kap as the default tool to solve various computation problems

The Kap programming language provides an alternative. Or rather, it's a work in progress so it's probably better to say it aims to provide an alternative. That said, I already use it for many tasks where a spreadsheet would be the go-to tool for many people.

Like most APL derivatives, the way you use it is often not by opening a test editor and start writing a program. Instead, you start it, you are presented with a prompt, and you start using it more as a calculator than a traditional programming language. This makes sense, since a large number of programming tasks that would take a lot of code in another language, can be solved in just a few characters in Kap.

Screenshot of the Kap welcome screen

When given a problem to solve, you typically look at the problem and then reach for the tool that is the most efficient way to solve that problem, given tool availability and the person's preferences. For example:

  • You need to compute the square of the number 54321. Most people would likely reach for their desktop environment's calculator here.
  • You want to compute the sum of some set of numbers. A natural choice here would be to use a spreadsheet.
  • You're given a dictionary and asked to find the longest word which contains no more than 2 vowels. I suspect a lot of people would open an editor and start writing Python code to solve this.

For me, in all of these cases I open the Kap interactive tool, type a few characters and the problem is solved.

This is how I solved the third problem, by the way:

{ ⊃ ⍵ ⊇⍨ ↑⍒≢¨ ⍵ /⍨ 2≥ (⊂"aeiou") (+/∊⍨)¨ ⍵ } io:read "dict.txt"

At this point, comments suggesting the code is “unreadable” are bound to be thrown around, but once you learn a handful of symbols, this is in fact significantly more readable than the kinds of formulas I see on a regular basis in Excel. This is proof that one is not more difficult to learn than the other, and that the problem is one of marketing rather than technological.

In case anyone is curious about the above code, the next blog post will be a breakdown of the code above to explain how it works. Check this blog in the coming days.

The point I am trying to make by showing this example at all is to try to evoke the same kind of response as beginners get when faced with any code of any kind.

The Kap approach to spreadsheet integration

The array editor

The array editor is a graphical tool that allows the user to edit arrays in a way that is similar to a spreadsheet. The goal of this tool is to be as familiar as possible to someone who has used spreadsheets in the past.

The tool also allows you to type Kap expressions to manipulate values in the sheet. This is not like formulas, but rather direct modification of the data.

Funnily enough, this is actually something that is not obvious how to do in Excel. If you have a set of numbers, and you want to add some constant to them, the way you'd do that is to create a formula that adds the constant to a cell and put that into a different location, usually on the same sheet, and then paste that formula to all the cells in an area with the same size as the original data, and hope that you got the dimensions right.

The array editor allows you to easily edit the content of an array. Of course, you can always do it programmatically, but I think the prevalence of spreadsheets has shown that it's convenient to do it in a visual manner.

Data import

I have been participating in discussion forums and chat channels about APL and other array languages for a while now, and when a beginner starts learning one of these languages, after they have gone through all the tutorials, there comes a time where they want to work with real data and the first question is: How do I get my data into the system?

After all, that's what I asked myself and was frustrated when I realised that preparing the data was harder than actually working with it.

So, when I started working on my own implementation, one of the most important goals was that loading pre-existing data into the interpreter was one of the most important features.

What's the most common format of array-based data today? It's probably not very far fetched to assume it's Excel. For this reason, being able to read spreadsheets into Kap has been a priority, and at the time of writing the following methods are supported:

  • Using functions such as msoffice:read function in the language itself. There are similar functions to load data in other formats, such as CSV and JSON.
  • Open the a spreadsheet file (Excel or LibreOffice) in the gui-based array editor.
  • Copy&paste directly from the spreadsheet.

By the way, you can also paste any table data into the array editor. Here's an example of using Wikipedia content:

Presentation

This is the part of the Kap UI tool that needs to most work. The vision is to provide a user interface that makes it just as easy to create a presentation of results as it is in Excel. However, it would only be used for presentation purposes, and not for data entry nor computation.

While there is some actual code there that can be tested, it's still not usable except as a demonstration of what the current ideas in this direction are.

The goal of Kap is to be a complement to spreadsheets

When someone needs to compute something, they typically look at the problem and then decide to open a tool that helps them solve said problem. While Kap is a great tool to solve a variety of problems, I tend to believe that it particularly excels in the kinds of problems where the solution would otherwise involve a spreadsheet.

Kap is a fully open source project available under the MIT license. If you want to learn more about Kap, visit the Kap website. Feedback is always welcome.

@loke@functional.cafe

Kap has a datatype called a list which is container that can hold 0 or more values. The list itself is a scalar (i.e. on a list returns an empty array).

The origin of the idea of the n-tuple type comes from the APL bracket index syntax:

    a ← 3 3 ⍴ ⍳9
    a[2;1]
7

It all came out of the idea that I didn't want special syntax for the argument to an axis index operation, so I made 1;2;3;4 an object of its own. This is the n-tuple.

a ← 3 3 ⍴ ⍳9
b ← (2;1)
a[b]

Once the n-tuple existed, using this as a general way to pass multiple arguments to functions was a logical step:

∇ foo (a;b) {
  a+b
}

This function can then be called as such: foo (1;2).

You may now be asking yourselves, “why is he talking about this stuff now?”

Well, there was one weirdness remaining. Support for empty elements in axis specifications, like so: a[1;] could still be improved. Until now, an empty value in an n-tuple: (1;;2;3) yielded (1 ; ⍬ ; 2 ; 3), but the had some special hidden property that was used by the index operation so the standard APL syntax worked. This is obviously really ugly.

This behaviour has now been formalised as null. The null value is a scalar which is very similar to ⎕NULL in Dyalog. An empty element in an n-tuple will now result in null. This also makes it possible programmatically create a value that can be passed to the index operation, which makes things consistent (although perhaps not very useful since squad already exists).

Properties of null

The null value is a scalar. In other words, it's a rank-0 object. The name null is not a regular symbol, but rather a syntactic element, which means it does not have a namespace.

It's type is null, which means that typeof null returns kap:null.

It's boolean value is true, in keeping with the specification that only the scalar value 0 is considered false, and all other values are true.

Next steps

Since there is now a proper null value in Kap, there will be some further changes to the language. Currently, several functions returns when there is no value to return. One such example is the return value from mapGet when the requested key does not exist. This will be changed to return null instead.

I'll start by commenting on what was said at the at the end of the episode of Array Cast with the title Tacit#5: They implied that the episode was long, and people probably wouldn't make it to the end.

In case any of the hosts are reading this: I know it was mentioned that you didn't want to hear if anyone felt the show was too slort. I'm going to say it anyway: The show is too short. Please make it at least 4 hours.

Deriving a function vs. returning a function

In the episode, there was a discussion around the difference between deriving a function and returning a function. I think this distinction was harder to make in the context of APL, because of two main reasons: APL does not have a separate parse step, and it does not support first-class functions. This makes it harder to explain precisely what the difference is.

To state the difference clearly: Deriving a function is a parse-time or compile-time operation, while returning one is a runtime operation.

Since Kap has first-class functions in addition to APL-style operators, I will use it to illustrate the difference.

Consider the following code:

+/ 1 2 3 4

While the above expression is parsed, +/ is processed and the resulting code is a call to the reduction function. The reduction function is an actual function which is created by the parser when it sees the reduction operator. This function contains a reference to another function (+ in this particular case) which then represents the “reduction over sum”.

Note that all of this happens before any code has been evaluated. It's just part of the parser.

By contrast, let's take a look at how to return a function. Here is the definition of the function foo which accepts a value, and returns a function which multiples its argument with that value:

∇ foo (a) {
  λ{⍵×a}
}

The λ symbol is Kap-specific syntax, and is used to represent a function as a value. The lambda symbol should be followed by any function, and the entire expression is then a simple scalar value that can be processed like any other value: it can be returned from functions, it can passed to other functions, it can be put inside arrays, etc.

Here is how we can create an array of two functions:

b ← (foo 2) (foo 3)

Here we have two calls to foo, each of which returns a different function (the first multiplying its argument by 2 and the second by 3). This is where functions are returned.

In Kap, to be able to call a function that is represented as a value, you use the “apply” symbol: . This derives a function from an expression:

⍞(b[0]) 5

Note that since deriving a function is a parse-time operation, the derivation doesn't know what b[0] contains. Thus, the derived function references the expression itself, and it's only when the derived function is actually called that the inner expression is evaluated. This yields the function reference which can then be called.

Or, to put it in a different way, the code above is a derived function (which is derived at parse time from the apply symbol). At runtime, this function evaluates an expression (dereferencing the first element in the array b) which returns a function which is then called.

Mention of Kap

Kap was mentioned very briefly in the context of syntax for the fork. In J and Dyalog, a fork has the syntax (A B C) where all elements are functions. Without repeating 5 episodes worth of discussions around this topic (please listen to them, they're interesting) the following expression: x (A B C) y is evaluated as (x A y) B (x C y).

As mentioned on the Podcast, Kap has a special syntax for this which uses special symbols: x A«B»C y. The benefit of this is that the regular 2-element train extends naturally to 3 or more elements. This eliminates the “even vs. odd” issue that causes a lot of confusion to anyone learning tacit programming.

A number of years ago I noted that Unicode did not contain all the characters in PETSCII, the character set used by the Commodore 64 and other classic Commodore computers. The Wikipedia page at the time even explained that some symbols were missing in their character table due to the fact that Unicode didn't support them.

I decided to make a post about it to the Unicode mailing list, and some people there agreed. The discussion expanded to talk about other 80's systems whose character sets were also missing.

So a working group was formed which I was a part of, and a proposal was made to add a number of characters which were used by several old systems. After a few failed attempts, the proposal was finally accepted, and it was included in Unicode 13.

I'm quite happy that my small contribution to this project helped fill a gap that I believe needed to be filled.

Let's use these symbols

A few years have now passed, and font support for these symbols is more widespread. This is good, because the character set contains symbols that are not just useful for interoperability with old systems, but are useful on their own. In particular, the BLOCK SEXTANT characters.

These are the 60 characters starting from U+1FB00, along with 4 more characters that already existed elsewhere. Here they are:

 🬀🬁🬂🬃🬄🬅🬆🬇🬈🬉🬊🬋🬌🬍🬎🬏🬐🬑🬒🬓▌🬔🬕🬖🬗🬘🬙🬚🬛🬜🬝🬞🬟🬠🬡🬢🬣🬤🬥🬦🬧▐🬨🬩🬪🬫🬬🬭🬮🬯🬰🬱🬲🬳🬴🬵🬶🬷🬸🬹🬺🬻█

Consider a character that is broken up into 3 rows by 2 columns. These characters are all 64 different combinations of blocks. In other words, we can use these to draw graphics at a resolution which is 2 times the width and 3 times the height of the terminal screen.

Kap is really good at working with arrays of data, so let's write a program to convert a 2-dimensional arrays of bits to a character array of BLOCK SEXTANT characters.

First we need to create the list of character above. Here's the code to do this:

blockMapping ← (@\u2590,)⍢(0x2A↓) (@\u258C,)⍢(0x15↓) @\uA0 , (@\u1FB00+⍳60),@\u2588

Let's break it down. The first part creates a list of the 60 characters in the legacy computing code block, and then prepends U+00A0 NO-BREAK SPACE, and append U+2588 FULL BLOCK. These two characters already existed in Unicode so they have different numbers:

@\uA0 , (@\u1FB00+⍳60),@\u2588

We then want to insert the two remaining symbols that also existed previously: U+2590 RIGHT HALF BLOCK and U+258c LEFT HALF BLOCK. We insert these using structural under.

Structural under deserves a block post of its own (and I've already written extensively about it), so for now we'll just note that the following inserts a at position b in c:

(a,)⍢(b↓) c

Now that we have the characters in a string, all we have to do is to merge each 3-by-2 block in the input array, decode them as a binary number and pick the appropriate character based on the resulting number. Here's the full function:

draw ⇐ {
	(2=≢⍴⍵) or throw "Argument must be a 2-dimensional array"
	v ← ((⍴⍵) + 3 2 | -⍴⍵) ↑ ⍵
	blockMapping ⊇⍨ (2⊥⌽)¨ ((¯2+≢v) ⍴ 1 0 0) ⌿ 3,⌿  ((¯1+1⊇⍴v) ⍴ 1 0) / 2,/v
}

The first line in the function simply raises an error if the input is not a 2-dimensional array. The second line extends the array so the width is divisible by 2 and the height is divisible by 3. Finally, the third line does all the work, computing the resulting image.

Let's try it:

io:print draw ⊃ {⍵⍴1}¨ ⌽⍳10

This should print:

███🬝🬀
██🬆  
🬝🬀   
     

The quality of the output depends on the font used, and I note that the default font used on write.as (where this blog is hosted) is not very good. With a proper font it looks a lot better.

Try the full example on the Kap web client.

Until now, the only way to return a value from a Kap function was to have it be returned as the return value from the last expression in a function.

Well, there are also exceptions, but those a bit special and should, well, exceptions.

But in some cases it's useful to return a value early. Essentially what return is in languages like C, Java, etc.

So, I did the obvious and implemented return just like in those languages, except that it's called to align with APL.

This example should be self-describing:

∇ foo (x) {
    if (x≡0) {
        →10
    } else {
        →20
    }
}

However, since is a regular function, it can also be called dyadically. So if the left argument is true, the right argument will be returned from the calling function. If it is false, then the function will return normally (returning the right argument). Confused yet? The word “return” was used in the description of both behaviours: to return from the current function, and returning a value from the return function itself.

Perhaps this example will make it more clear:

∇ foo (x) {
    (x≡0) → 10
    20
}

Future work: Returning from different scopes

Common Lisp has a function RETURN-FROM that allows the for returning from an outer function, for example when using nested functions. It also provides a BLOCK form that creates a named scope that can be explicitly returned to.

This is a very useful feature, and implementing it in KAP would not be particularly complicated.