Feature api#115
Open
ascottDI wants to merge 4 commits into
Open
Conversation
added 4 commits
July 15, 2026 14:45
…n each API to get collected
…lky inside other processes
| / case-insensitive; returns an unkeyed table. | ||
| if[-11h=type s;s:$[null s;"*";"*",string[s],"*"]]; | ||
| if[not 10h=abs type s:s,(); | ||
| .z.m.logerr[`find;err:"di.api: search pattern must be a symbol or string"]; |
There was a problem hiding this comment.
The find function converts the search pattern s to a list with s,() and then applies like with a scalar pattern. In q, like expects a scalar string pattern on the right-hand side, but after s:s,(), s is a list (e.g. enlist "*foo*"). A list pattern passed to like will produce a type error at runtime. The ,() coercion should be removed; the scalar string s (already ensured to be a string by the preceding guards) should be passed directly to like.
DIReview Summary0 critical | 1 warning(s) | 0 suggestion(s)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
di.api — TorQ Modularisation PR
Summary
Extracts a process's callable-function registry from TorQ's
.apinamespace into a standalonekdb-x module:
di.api. The module holds one entry per public/registered function — its name, apublic flag, and human-readable description / params / return text — and exposes a small query
surface over it (
getapi,find/f/p). Registration is push-based:di.torqcollects eachmodule's metadata at startup and registers it here via
add; every module advertises its ownmetadata through a
getapimetaexport. It satisfies the di.* contract: a required injected logger,a minimal exported API, no hard module dependencies, and a k4unit suite.
Background
TorQ's
.apimarries two things: a registry of documented functions and live introspectionof the root namespaces (
\v/\fscans, grepping function definitions, sizing variables). In themodule world only the first half fits — module code lives in each module's private
.z.m, not inscannable root namespaces, so a live scan would see only a process's top-level glue, not the module
logic it exists to inspect.
So
di.apiis registry-only: it is the single source of truth for the process's API, populatedexplicitly rather than discovered.
di.torqreads each module'sgetapimetaat startup and callsaddfor every row (applying process-wide name qualification); consumers query withgetapi/find.di.apidepends on no other module, and no module depends ondi.api.Changes
New files
di/api/api.qinit,add,getapi,find,f,p,getapimeta, and thedetailschemaregistry templatedi/api/init.qapi.q, declares the export listdi/api/test.csvbefore/runsetup)di/api/api.mdDifferences from the TorQ original
.apidi.api\v/\f, definition grep)detailtabledi.torqcallsaddper module, sourced from each module'sgetapimeta.lg.o/.lg.elogdep, required atinit, no fallback and no adaptationadd/findlog via.z.m.logerrthen signal;initsignals directly (no logger wired yet) — all with adi.api:prefixusesingleton,init[deps],export:listLogging contract (injected dependency)
Consistent with the settled no-
normlogdirection across di.*: logging is an injecteddependency, wired at
init, with no default logger and no adaptation inside the module.initmust be called before any other function.init[deps]takes a dict carrying the requiredlogkey. It errors immediately (plain signal,di.api:prefix) ifdepsis not a dict, is missing`log,logis not a dict, orloglacks any of
`info`warn`error.logvalue must already be a binary`info`warn`error!{[c;m]}dict (context symbolc, message stringm). Build it fromdi.logor hand-roll one; a raw monadickx.loginstance must be wrapped by the caller first —
di.apidoes not wrap it.initfans the injectedlogdict out into module-local.z.m.loginfo/.z.m.logwarn/.z.m.logerr.add/findlog-then-signal on bad input so everyrejection is observable in the log as well as thrown.
Exported API
name— a symbol; the registry key. Re-adding the same name updates it in place.public— a boolean;addrejects a non-boolean.s(pattern) — a symbol (`= all, else a*s*substring) or a string glob; case-insensitive.p(flags) — public flags to include (1bpublic-only,01ball).Registry schema
detailschemais the constant template;.z.m.detailis the live copy, reset to empty on eachinit.Key design decisions
.z.m, invisible to\v/\f, so a scan is worse than useless — it would report a process's glueas its API. The registry is populated explicitly instead, making it the single source of truth.
getapimeta). Rather than a central hand-maintained list, each moduledeclares its own API as data — one
(name;public;descrip;params;return)row per export — anddi.torqcollects and registers them. Names ingetapimetaare bare (the module's own);di.torqapplies process-wide qualification when it callsadd. This module dog-foods theconvention:
getapimetadocuments its own seven exports.getapimetais built with therow-oriented
flip cols!flip(rows)idiom — one self-contained line per function — rather than fiveparallel column-lists, so it stays terse and a function can't drift out of column alignment. It
costs the module nothing at runtime: it's a function, materialised once when
di.torqcalls itat startup, then registered centrally in the
di.apiregistry; the module keeps no resident copy.This is the form the modularisation skill now mandates for every module.
findunifies the query surface.fandpare projections offind[;01b]/find[;1b], sothere's one matching implementation. Symbol patterns become
*substring*,`matches all, andstrings pass through as globs — all case-insensitive.
di.torqwill actually register. TorQ's introspection helpers are dropped rather than stubbed (below).
Not ported from TorQ's
.api(deliberate)The live-introspection half does not fit the module world (module code isn't in root namespaces):
u.q/.Q/.h/.o" filtered out of the live scan; here the registry holds only registered module functions, sou==p.search/swhereamifullapi(namespace scan)detailmodel doesn't apply;getapi/findserve the registry directly.mem/mdi.memstats.exportconfig/exportallconfig/torqnamespacesgetmodule) joined with descriptions (di.apigetapi/find). Both modules are standalone and don't depend on each other, so the join belongs in di.torq (it has both) — a di.torq-era task, not a di.api change.Test coverage —
test.csv(k4unit), 24 assertions (33 rows incl.before/runsetup), all greeninit— dependency validationdeps, missinglogkey, non-dictlogvalue,logdict missing a keyadd/getapiinit;addstores one entry; public flag and description stored; re-adding a name overwrites in place (no duplicate); rejects non-symbol name; rejects non-booleanpublicfind/f/pfwith a null symbol returns all,preturns only public; substring match; case-insensitive; string-glob pattern;fincludes non-public entries whilepexcludes them; rejects a non-symbol/non-string patterngetapimetafind) is public while framework plumbing (init) is notRunning the tests
export QPATH=/path/to/kx/mod:/path/to/kdbx-modulesCaveats — not deployable in a live process until two things land
di.apiis complete and tested as a standalone unit. It cannot yet run inside a live processbecause:
di.log(the standard logger) is not merged and not final.di.apirequires a conformingbinary
`info`warn`errorlogger injected atinit;di.logprovides exactly that, but itcurrently lives on the
feature-loggingbranch and its contract isn't approved. If that contractchanges, re-verify — no di.api code change is expected. Interim callers can hand-roll a logger
(the tests do).
di.torqdoes not exist yet. The orchestrator that reads each module'sgetapimeta, appliesprocess-wide name qualification, and calls
addfor every row hasn't been built.addandgetapimetaare unit-tested for that role, but the real collect-and-register flow is unprovenuntil
di.torqlands.Deferred follow-ups
di.logintegration test oncedi.logmerges (ause`di.logtest can't becommitted on this branch yet).
versionexport +deps.qwhendi.depcheckships (repo-wide versioning rollout — nomodule carries these yet).
exportconfigequivalent indi.torq(config values × api descriptions acrossnamespaces), which is where both inputs are available.