Skip to content

Instantly share code, notes, and snippets.

@razetime
Last active January 18, 2024 06:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save razetime/fe45ed656e45ed7894c889de0d560b55 to your computer and use it in GitHub Desktop.
Save razetime/fe45ed656e45ed7894c889de0d560b55 to your computer and use it in GitHub Desktop.
chapter 15

Tables and Libraries

ngn/k has a hidden, somewhat secret data structure: the table. Tables are a prominent presence in k7 and k9, the latest versions of K, so ngn/k supports them for that reason.

Tables

A table uses symbols to mark columns, and integer indices to mark rows. Making a table is simple. The first method is flipping a dictionary1:

 :t:+`a`b!(1 2;3 4)
+![ `a `b
 +(( 1; 3)
   ( 2; 4))]

The display is a bit strange because ngn/k as described earlier, always returns valid K data. However, as long as you have more than one column in your table, the columns will be aligned correctly.

The other way to make a table is with a list of dictionaries:

 :t:(`a`b!1 2;`a`b!3 4)
+![ `a `b
 +(( 1; 2)
   ( 3; 4))]

The key names of every element must be the same, and they must be in the same order. This form is good if you are constructing tables programmatically, so (,`a`b!1 2),`a`b!3 4 would be a different way to come up with the same table.

Creating a K table requires rectangular data: all rows must be of the same length, and all columns must be of the same length.

Tables can be indexed into in the same ways as other datatypes, described in Chapter 4. However, tables are special in that you can index into them using column labels and row labels, without the need of a projection. Using t from the previous example:

 t[1]
!/+((`a;3)
    (`b;4))
 t[`b]
2 4

Here, retrieving a single row returns a dictionary, and a single column is a simple list. Retrieving multiple rows and columns results in a table.

ngn/k does not come with a query language for tables, but there are equivalent ways to access data. The basic idea is that indexing lets you filter columns, and other primitives work on rows. function#table is similar to an SQL SELECT, for example.

File Model

ngn/k's file I/O is based on the UNIX model of file management. It is best to read up on the mechanics of the model using the UNIX manual pages or other reliable document before using K for file I/O.

As you may remember from Chapter 10, we can use 0:(line-oriented) and 1: (byte-oriented) to read from and write to files. This is easy with a string denoting a file name, but you can also open a file descriptor to a file with <.

<x Open

Symbol: <

Args: < symbol

Description: Return a file descriptor that points to the file at location x.

>x Close

Symbol: >

Args: > integer

Description: Close the file descriptor with id x.

File descriptors can be used in place of file names for 0:. You can also use file descriptors that were not opene by your program. A simple example is opening stdin on linux, which is usually open at file descriptor 0. So this is another way to prompt for input:

 0:0
123
,"123"

Libraries and Namespaces

A somewhat common approach in a language like K is to store some forms of processed data in a file for later analysis (similar to prolog/datalog, the data format is consistent and easy to read). In any K file or the REPL you can do \l data.k to reuse the data from a file, or simply use the file like a library.

Sometimes however, your code and your data files may have some naming conflicts, and this is where \d comes in. One easy way to use \d is as an easy way to make large namespaces. Once \d ns is used for example, all global variables defined after that are prefixed with ns..

 \d ns
 a:1
 ns
'value
 ns
 ^

 ns.a
1

Note that ns is not a variable you can inspect, it is just a prefix for variables in your namespace. You can freely use any variables defined in ns as long as you don't navigate to a different one with \d, like \d ., which goes back to the global namespace.

 \d .
  a
'value
 a
 ^
i
 ns.a
1

A common pattern for importing libraries is hence:

\d l
\l lib.k
\d .

You can load as many libraries as you like into a namespace, allowing some useful grouping strategies.

Finally, to "index" into namespaces, you can use .. This builtin is mostly for convenience.

.x Get

Symbol: .

Args: . list_of_symbols

Description: Index into the given namespaces. .`a`b`c will return the value of a.b.c. Invalid routes will throw an error.

Serialization

The other way of serializing K data, apart from the default prettyprinter `k is in the JSON format. K's data model generally maps well to JSON. You can convert any object to JSON with the help of `j.

 `j@`a`b`cd!(1;"a";`sym)
"{\"a\":1,\"b\":\"a\",\"cd\":\"sym\"}"

These special functions like `j and `k are symbols that behave specially when called on data, and are special to the ngn/k implementation.

1: Internally this is not a full transposition of a dictionary. It just changes how the data is seen by the ngn/k interpreter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment