In our application, the .z.u
function (which returns the username) was originally treated in a case-sensitive manner—so Test2001
and test2001
were considered two distinct users. We've recently decided to make usernames case-insensitive, which required updating all occurrences where .z.u
was used (and there were quite a few).
Given this, I'm wondering what the best practice is for using functions like .z.u
, and more generally, functions from the .z
and .Q
namespaces.
Would it make sense to define a wrapper function like:
.util.extractUser:{lower .z.u}
to centralize this behavior?
And if so, should we consider wrapping other commonly used .z
or .Q
functions as well, in case we need to modify their behavior later (e.g. due to changes in business logic or updates in kdb+ itself)?
In our application, the .z.u
function (which returns the username) was originally treated in a case-sensitive manner—so Test2001
and test2001
were considered two distinct users. We've recently decided to make usernames case-insensitive, which required updating all occurrences where .z.u
was used (and there were quite a few).
Given this, I'm wondering what the best practice is for using functions like .z.u
, and more generally, functions from the .z
and .Q
namespaces.
Would it make sense to define a wrapper function like:
.util.extractUser:{lower .z.u}
to centralize this behavior?
And if so, should we consider wrapping other commonly used .z
or .Q
functions as well, in case we need to modify their behavior later (e.g. due to changes in business logic or updates in kdb+ itself)?
2 Answers
Reset to default 1.util
namespaces are common. Items get added on case by case basis.
Example: https://github/BuaBook/kdb-common/blob/master/src/util.q#L80
/ @returns (Boolean) If the current process is in debug mode or not
.util.inDebugMode:{ :1i = system "e" };
/ @returns (Boolean) True if the process is bound to a port, false if not
.util.isListening:{ `boolean$system"p" };
In general, .z
functions (not .z variables) are encouraged to be overwritten as is. For example .z.pg, .z.ps, .z.pc, .z.po
etc. In fact you can't trigger such callbacks without modifying the vanilla functions. Your example of .z.u
is a bad example, it's not a function so you can't replace it with a function/logic of your own and even if you could it ignores overwrites:
q).z.u
`tlynch
q).z.u:`ABC
q).z.u
`tlynch
For .Q
functions it is possible to overwrite and/or create your own but it is usually discouraged.
In terms of how to handle a codebase which references a lot of these built-ins, personally my preference it to allow each individual script/user use the known built-ins rather than soulless .util functions. Otherwise reading the code becomes a tedious exercise of .util lookups (who needs .util.plus?!). Of course that then means that to control certain behaviour (particularly after it's already written) you need gain control of the primitive, with the alternative being to modify many scripts in many places. Again, it's often down to personal preference since in most cases it's possible to do it either way.
One final point to add, sometimes it can be useful to retain vanilla behaviour when gaining control over a built-in. One sensible way to do this is to pass the vanilla function into your custom function and applying the vanilla function within yours. For example
.z.pg:{[orig;x] myotherlogic[];orig x}[.z.pg;]
This essentially snapshots the existing .z.pg
as "orig" and applies that built-in logic within your custom function. (just be careful if this override runs twice then you'll be snapshotting the custom logic, not the original)