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.