Summary
Simplify the Primitive.implementation carrier type from
InferenceContext -> Graph -> [Term] -> Either Error Term
to
[Term] -> Either Error Term
so that it mirrors PrimitiveDefinition.defaultImplementation and a host's
primitive shell can be derived directly from the canonical Hydra term.
Motivation
The current threaded shape is a historical artifact. The interpreter
already calls Strip.deannotateTerm on every argument before invoking
the primitive (and, in the reducer path, reduces it), so an
implementation can pattern-match the arguments directly:
- It never needs to dereference a free
TermVariable. If an argument is
still a variable when the implementation runs, the failure happened
upstream — the primitive should not paper over it via a graph-aware
extractor.
- It never needs to call back into the reducer. A higher-order primitive
(e.g. lists.foldl, eithers.bimap) can return an unreduced
applicative term and let the interpreter's outer reduce fold it.
Carrying cx/g only so a primitive can shortcut that reduction is a
perf optimization that does not belong in the universal API.
Crucially, the typed PrimitiveDefinition.defaultImplementation lives at
the public signature of the primitive (e.g. for eithers.foldl:
(a -> b -> Either c a) -> a -> [b] -> Either c a). It must be a pure
Hydra term with no InferenceContext / Graph parameters. Aligning
Primitive.implementation to the same shape closes the type mismatch
between the canonical reference implementation and the host-side
primitive shell — the same mismatch at the heart of #437.
Scope of change
Hydra.Sources.Kernel.Types.Graph — change the implementation
field of the Primitive record.
Hydra.Dsl.Prims — adjust the prim1/prim2/prim3/... wrappers
(today they thread cx/g into termCoderEncode/Decode; the new
carrier no longer accepts them, so the encode/decode plumbing needs
to obtain those values another way, or be lifted out of the
per-primitive adapter).
- Host code generators that emit primitive shells (Java, Python, Scala,
TypeScript, Lisp family, Go) — drop the leading cx, graph parameters
from emitted implementation() signatures.
Hydra.Sources.Libraries — hydraLibLists and other registries that
call prim* with the current shape.
bindings/ — any externally-registered primitive (e.g. RDF, Neo4j
bindings) registered against the current shape.
Hydra/Sources/Kernel/Lib/Defaults/*.hs — entries currently typed
TypedTermDefinition (InferenceContext -> Graph -> ... -> Either Error Term)
collapse to TypedTermDefinition (... -> Either Error Term).
Sequencing
This should land after the defaultImplementation integration
sweep tracked in #437. The Defaults rewrite is the natural place to
surface the design problem in context (writing 9 modules of Defaults
entries makes the unused parameters glaringly evident); attempting the
parameter drop ahead of that sweep would force two passes over the same
code.
Target: shortly after the 0.16.0 release.
Compatibility
This is a breaking change for any out-of-tree primitive registration.
The in-tree surface (kernel Lib/*.hs, host generators, overlays) all
change in lockstep with the carrier-type rename.
References
Summary
Simplify the
Primitive.implementationcarrier type fromto
so that it mirrors
PrimitiveDefinition.defaultImplementationand a host'sprimitive shell can be derived directly from the canonical Hydra term.
Motivation
The current threaded shape is a historical artifact. The interpreter
already calls
Strip.deannotateTermon every argument before invokingthe primitive (and, in the reducer path, reduces it), so an
implementation can pattern-match the arguments directly:
TermVariable. If an argument isstill a variable when the implementation runs, the failure happened
upstream — the primitive should not paper over it via a graph-aware
extractor.
(e.g.
lists.foldl,eithers.bimap) can return an unreducedapplicative term and let the interpreter's outer
reducefold it.Carrying
cx/gonly so a primitive can shortcut that reduction is aperf optimization that does not belong in the universal API.
Crucially, the typed
PrimitiveDefinition.defaultImplementationlives atthe public signature of the primitive (e.g. for
eithers.foldl:(a -> b -> Either c a) -> a -> [b] -> Either c a). It must be a pureHydra term with no
InferenceContext/Graphparameters. AligningPrimitive.implementationto the same shape closes the type mismatchbetween the canonical reference implementation and the host-side
primitive shell — the same mismatch at the heart of #437.
Scope of change
Hydra.Sources.Kernel.Types.Graph— change theimplementationfield of the
Primitiverecord.Hydra.Dsl.Prims— adjust theprim1/prim2/prim3/... wrappers(today they thread
cx/gintotermCoderEncode/Decode; the newcarrier no longer accepts them, so the encode/decode plumbing needs
to obtain those values another way, or be lifted out of the
per-primitive adapter).
TypeScript, Lisp family, Go) — drop the leading
cx, graphparametersfrom emitted
implementation()signatures.Hydra.Sources.Libraries—hydraLibListsand other registries thatcall
prim*with the current shape.bindings/— any externally-registered primitive (e.g. RDF, Neo4jbindings) registered against the current shape.
Hydra/Sources/Kernel/Lib/Defaults/*.hs— entries currently typedTypedTermDefinition (InferenceContext -> Graph -> ... -> Either Error Term)collapse to
TypedTermDefinition (... -> Either Error Term).Sequencing
This should land after the
defaultImplementationintegrationsweep tracked in #437. The Defaults rewrite is the natural place to
surface the design problem in context (writing 9 modules of Defaults
entries makes the unused parameters glaringly evident); attempting the
parameter drop ahead of that sweep would force two passes over the same
code.
Target: shortly after the 0.16.0 release.
Compatibility
This is a breaking change for any out-of-tree primitive registration.
The in-tree surface (kernel
Lib/*.hs, host generators, overlays) allchange in lockstep with the carrier-type rename.
References
PrimitiveDefinitionsplit out from implementationFlowmonad removal that produced the currentEither-based shapeInferenceContextreplaced legacyContextLib/Defaults/term-level implementations not wired intoPrimitiveDefinition.defaultImplementation#437 —defaultImplementationintegration (this issue's prerequisite)