Entries in type error (2)

I defined an untyped list, but I get a type error when I add more than one element to it? How do I store mixed data in a list?

If you are defining the list's contents at the same time you declare it, this is easy:

q)list: (`sector; 47)
q)type list
0h
q)list ,: 2.7
q)list
`sector
47
2.7
q)

However, you often don't know what items are going in the list in advance. Let's try defining an empty list without specifying an element type:

q)list: ()
q)

At first, that seems to be just what we wanted:

q)type list
0h
q)

The trouble begins when we actually add elements to the list:

q)list ,: `sector
q)list ,: 47
'type
q)

What happened? Let's re-examine list's type:

q)type list
11h
q)

Now list's type is no longer 0; it has been transmuted into 11; i.e., list of symbol. The 'type error is telling us that we can't add an int to a list of symbol.

In some situations, determining the type of an empty list dynamically (i.e., when the first item is added) may be what you want. A list of symbol (or int) uses less memory that an untyped list with the same number of elements. Most operations on a typed list will be faster as well.

In this case, however, we need a way to prevent q from promoting the untyped list to a typed list. The only way to do that is to stuff the list - when you create it - with some data that can't fit into a typed list. While you could use two values of different types (as in our first example), you only need to use one value if that value is itself a list. Our practice to use a list of characters, i.e., a string:

q)list: enlist "sentinel"
q)type list
0h
q)list ,: `sector
q)list ,: 47
q)count list
3
q)

Note that the use of enlist is essential; without it, list is simply the string:

q)list: ("sentinel")
q)type list
10h
q)count list
8

We use the string "sentinel" because the term sentinel has been used for decades in computer science to denote a value in a data structure that isn't actually part of the data, but rather signals some special condition to the program. It also happens to be a term that doesn't come up often in other domains.

Of course, any code that reads or writes to list needs to understand that the first element is to be ignored. (In our experience, the less code that is aware of that, the less time we spend debugging.) In particular, you must never remove all the data from list; the sentinel must always remain. Otherwise, when you start adding elements again, list's type will become list-of-type-of-the-first-element-added:

q)list: list _/ reverse til count list
q)count list
0
q)type list
0h
q)list ,: `sector
q)type list
11h
q)

This can happen unexpectedly when a mixed-type list is actually a column in a table. See this related faq.

How do I store mixed data types in a single column of a table?

The first time we needed a column that can hold values of multiple types, we tried something similar to the following:

q)t: ([] thing: ())
q)meta t
c    | t f a
-----| -----
thing|
q)

True, when you define a table column as an untyped list, it can contain anything. However, the first time you put something in it, the column's type is set to the type of that first inserted item:

q)`t insert `aunt
,0
q)`t insert 33.33
'type
q)meta t
c    | t f a
-----| -----
thing| s

In our example, the type of the thing column has been promoted to symbol; we can't insert numbers into it.

When you want to store elements of different types in the same column, you have to prevent q from performing this type promotion by stuffing a sentinel row (i.e., a row of dummy values), whose value for the column in question is itself a list, into the table:

q)t: ([] thing: enlist "sentinel")
q)meta t
c    | t f a
-----| -----
thing| C

Don't believe the hype. The meta function is trying to help out by actually inspecting the first value in the column when reporting the thing column's type. However, the true type of the column is untyped, i.e., zero:

q)type t `thing
0h
q)

You can, therefore, put whatever you like into t's thing column:

q)`t insert/: (`gave; 42)
1
2
q)t
thing
----------
"sentinel"
`gave
42
q)meta t
c    | t f a
-----| -----
thing| C
q)

Beware cleaning out such a table too thoroughly (in, say, an end-of-day function):

q)delete from `t
`t
q)meta t
c    | t f a
-----| -----
thing|
q)

See this related faq on untyped lists.