Skip to content

Drop InferenceContext and Graph parameters from Primitive.implementation #446

@joshsh

Description

@joshsh

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.LibrarieshydraLibLists 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions