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 0h; it has been transmuted into 11h; 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
q)

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.