Entries in null type (2)

Why does the type of null returned by a failed dictionary key lookup vary?

Short answer: A failed dictionary search returns the null corresponding to the first dictionary entry's value.


Let's test with the following two dictionaries, sfdict and fsdict:

q)sfdict: `symbol`float ! (`abc; 123.4)  // type sym is first
q)fsdict: `float`symbol ! (123.4; `abc)  // type float is first
q)sfdict
symbol| `abc
float | 123.4
q)fsdict
float | 123.4
symbol| `abc
q)

Note that while both dictionaries contain the same key-value pairs, because dictionaries are an ordered list of key value pairs, the above two dictionaries do not match.

q)sfdict = fsdict
symbol| 1
float | 1
q)sfdict ~ fsdict
0b
q)

A dictionary search returns a null value when the key sought is not found:

q)null sfdict[`badkey]
1b
q)null fsdict[`badkey]
1b
q)

Getting a null back from a failed lookup is expected. On the other hand, many are surprised to find that the two nulls returned for the above two lookups differ:

q)sfdict[`badkey] ~ fsdict[`badkey]
0b
q)sfdict[`badkey]
`                    // null symbol
q)fsdict[`badkey]
0n                   // null float
q)

The types of the returned nulls are different according to the types of the first values in sfdict and fsdict. This is the same behavior observed when indexing a mixed list with an out-of-range index:

q)(`a; `b; 1f; 2f)[5]
`
q)(1f; 2f; `a; `b)[5]
0n
q)

There's a similar "first element wins" behavior with the atomicity of dictionary keys. see this faq on dictionary indexing to learn more.

Given a value, how do I get the null of that value's type?

We take advantage of a property of q's indexing: If you request an item from a list (or a dictionary, for that matter) using an index that is out of range, q returns the null item for the list's type.

q)x: 1 2 3
q)x 3
0N
q)

Thus, we can take the item whose corresponding null we want, make a list from it, and then access that list with an out-of-range index:

q)NullOf: {[item] enlist[item] 1}
q)NullOf 1
0N
q)(::) ~ NullOf {x*x}
1b
q)null NullOf (.z.D; 1e; " "; type "foo")
1111b
q)

Remember, there is no null list. If you pass a list to NullOf, you get the empty list of the corresponding type:

q)NullOf 1 2 3
`int$()
q)

See also: .Q.ty