The Python surface's shrink ledger
KERNEL.md asks which translator heads are primitive and which are derived, and requires every derived form still fused into the compiler to say why. This page asks the same of the library, and is generated by extensions/python/tools/ledger.py from the code rather than maintained by hand.
Space publishes 113 methods.
| class | count | what it means |
|---|---|---|
| primitive | 24 | speaks to the engine; nothing on this surface expresses it |
| derived | 15 | its body reaches another PUBLIC method, so it is macro-expressible by one |
| shared | 26 | reaches a private helper: an implementation already collapsed, which is a shrink's outcome rather than its target |
| facade | 39 | delegates to a satellite module; the layering, not a duplication |
| property | 9 | no body to class |
The derived methods, and what each buys
A derived method is not a defect. A named form of one mechanism is derived and should stay, because collapsing it would put the mechanism's argument back at the call site. What the ledger refuses is a derived method with no answer.
| method | expressible by | what it buys |
|---|---|---|
add_tagged_fact | add | the tag threader's atom shape, so a caller writes the tag and the proposition rather than the (fact tag proposition) spelling, and gets the stored atom back where add answers nothing |
add_tagged_rule | add | the same for a rule, whose atom the algebra generates from a head and its premises rather than from a literal |
copy | atoms | one act for enumerate-and-store, and the space it answers is a new one rather than this handle rebound |
doc | eval | the structured (@doc ...) answer decoded into its parts, where eval of get-doc answers the raw term |
eval | answers | the EAGER path. It delegates to answers only for the carrier, the theory and the interpreter, and the split is measured rather than assumed: routing it through the cursor unconditionally left a memoized definition's call keys unrecorded, 13 entries on the eager path against 0 through the cursor |
ne | eq | the negated relation as one term, (not (== a b)), so a caller spells the relation rather than its expansion |
pre_add | define | a HOOK rather than an equation: the definition it installs runs on the write path, which is a different moment from a call |
register_foreign_library | register_prolog | the library PATH handling around a registration, so a caller names a shared object rather than resolving it |
solve | eval | the relational direction and the answer template. It derives the template from the pattern's variables followed by any new subject variables, which is what lets either direction introduce the bindings and removes let's hand-written third argument |
subs | map | substitution keyed by ATOM, and an answer Row accepted directly. map takes a transform over every node; this is the transform, and the key carries whether a variable or a symbol is meant |
transaction | eval | all-or-nothing over the block, which no sequence of evaluations gives: the engine's own transaction boundary is what makes a failure leave the space unchanged |
transactional | transaction | the DECORATOR shape, so each CALL of the wrapped function runs in its own transaction. transaction runs one callable now; decoration cannot await and cannot run anything |
type | eval | the first get-type answer decoded, including the undefined case, where eval answers the raw term list |
watch | subscribe | the ITERATOR form of the callback method, and a deadline. A caller who wants to pull events cannot use a callback, and the two consumption models are why both exist |
why | match | the question without the answers. It asks why a pattern matches nothing without the caller first materialising the match that did not happen |