-
Notifications
You must be signed in to change notification settings - Fork 24
Packaging
This page describes Hydra's packaging model: the types that organize Hydra programs into definitions, modules, and packages, and the metadata attached to each. It explains the model as a concept; for where this code physically lives, see Code organization, and for the kernel type breakdown see Implementation.
The packaging types are defined in
Hydra/Sources/Kernel/Types/Packaging.hs,
in the hydra.packaging namespace.
Hydra organizes code as a three-level containment hierarchy:
- A package is a named, independently versioned collection of modules.
- A module is a named collection of definitions sharing a namespace.
- A definition binds a name to a term, a type, or a primitive.
Package
├─ metadata, dependencies (on other packages)
└─ Module*
├─ metadata, dependencies (on other modules)
└─ Definition* (term | type | primitive)
└─ metadata
Every level — package, module, and definition — can carry the same optional
EntityMetadata (documentation and lifecycle information).
This uniformity is deliberate: see Why metadata is bundled.
A Definition is the smallest named unit. It is a union of three cases:
- A term definition (
TermDefinition) binds a name to a term, with an optional type signature. When the signature is absent, it is inferred. - A type definition (
TypeDefinition) binds a name to a type scheme. - A primitive definition (
PrimitiveDefinition) declares a built-in function or constant: its name, an always-explicit signature, purity and totality flags, and an optional cross-compilable default implementation expressed as a Hydra term.
Each definition carries its own optional EntityMetadata.
From the packaging point of view, a primitive is simply one kind of definition, declared independently of any host language; for how primitives are added and implemented, see Adding a primitive.
A Module is a logical collection of definitions in a single namespace.
It has:
- a name (a
ModuleName, e.g.hydra.core), which is the common prefix for every definition name in the module; - optional metadata;
- a list of dependencies on other modules;
- the definitions themselves.
A module name is both an identity and a namespace prefix.
A QualifiedName pairs an optional module name with a mandatory local name, which is how a
definition within a module is referred to from elsewhere.
A ModuleDependency names a depended-on module and, optionally, the package that provides it.
When the package is omitted, the resolver searches all packages in scope.
A module name that is ambiguous across packages is a resolution error, which can be disambiguated
by naming the intended package explicitly.
A Package is a named collection of modules — the unit of distribution and versioning.
It has a name (a PackageName, e.g. hydra-kernel), optional metadata,
a list of package-level dependencies, and its modules.
A PackageDependency names another package and constrains it with a VersionSpecifier.
A Version is a version string such as "0.15" or "1.0.0".
The version specifier is intentionally minimal today: the only variant is any (any version
satisfies the dependency).
Further variants such as exact, caret, and range can be added later without breaking
consumers of the any form — an instance of the forward-compatibility principle that runs
throughout the packaging model.
Any packaging entity — a package, a module, or a definition — may carry an optional
EntityMetadata record. It bundles four independently optional kinds of information:
- description — an optional, concise one-line human-readable summary of the entity.
-
comments — zero or more long-form prose notes: cross-cutting semantic conventions,
caveats, and references that would otherwise be repeated across the entity's constituents.
For example, a comment on the
hydra.lib.charsmodule can state once that all of its primitives interpret their argument as a Unicode code point, rather than repeating that on every predicate. - seeAlso — typed cross-references to related entities, for navigation and documentation.
- lifecycle — optional version-lifecycle milestones (see below).
A LifecycleInfo records version milestones for an entity. Each milestone is independently optional:
-
availableSince — the
Versionin which the entity was introduced. -
deprecatedSince — the
Versionin which the entity was deprecated, if applicable.
Further milestones (for example stableSince or removedSince) can be added later without
changing the types that carry the lifecycle.
The seeAlso field holds a list of EntityReference values.
An EntityReference is a typed pointer to a packaging entity: a package (by PackageName),
a module (by ModuleName), or a definition.
A reference to a definition is a DefinitionReference, which names a type, a term, or a
primitive by its Name.
Because these references are typed rather than free-form strings, tools can resolve and
validate them — for navigation, documentation, or impact analysis.
Earlier versions of the model attached documentation directly to each entity — for instance,
a module carried a description field of its own.
Adding any new kind of metadata then meant adding a parallel field to every entity type that
should carry it, changing the shape of Module, Package, and each definition type in lockstep.
Bundling all such information behind a single optional metadata field of type EntityMetadata
decouples what metadata exists from which entities carry it.
New metadata — another comment category, a new lifecycle milestone, a new kind of cross-reference —
is added inside EntityMetadata once, and every entity gains it without any change to its own
field shape.
This is a backward-compatibility measure: it lets the packaging model grow without breaking the
encoded shape of existing packages, modules, and definitions.
The same principle motivates the open-ended VersionSpecifier and LifecycleInfo.
-
Code organization — where packaging code lives (
packages/,heads/,dist/). - Concepts — the broader LambdaGraph data model and type system.
- Adding a primitive — primitive definitions in practice.
- Implementation — the kernel's type-module breakdown.