Navigation

Generated Datatypes

Generated datatypes make structure explicit. A datatype is not only a constructor API; it also carries the description and metadata that the kernel, generic walkers, diagnostics, schemas, dependency extraction, and ornaments reuse.

Use generated datatypes when a domain value should be more than an untyped attrset. The generated value has constructors for building data, a type for checking data, and public structural evidence for tools.

A first datatype

let
  H = fx.types.hoas;

  Aspect = H.datatype "Aspect" [
    (H.con "aspect" [
      (H.field "name" H.string)
      (H.field "target" H.string)
      (H.field "requires" (H.listOf H.string))
    ])
  ];
in Aspect

The generated Aspect object exposes:

  • Aspect.D — the description used by the kernel.
  • Aspect.T — the generated type.
  • Aspect.aspect — the constructor.
  • _dtypeMeta — constructor and field metadata.

The description is the important part. It is the common shape that lets multiple consumers agree on what an aspect declaration is.

View and review

Application code usually works with named constructor records:

let
  G = fx.types.generic;

  aspect = G.value.review Aspect.T {
    _con = "aspect";
    name = "workspace-shell";
    target = "module";
    requires = [ "toolchain" ];
  };
in
  G.value.view Aspect.T aspect

review turns a constructor record into the generated value. view turns the generated value back into a constructor record. This is the bridge between ergonomic Nix data and the description-backed representation the kernel can inspect.

Generated families

The datatype layer supports several shapes:

  • datatype for ordinary generated datatypes.
  • datatypeP when constructors are parameterized.
  • datatypeI when the family is indexed.
  • datatypePI when both parameters and indices are needed.

The more indexed the family, the more precisely the type can state what the value means. The public model is still the same: a description, a generated type, constructors, and metadata.

Stack-safe elaboration

Generated constructor trees can be large. nix-effects elaborates them through the same stack-safe machinery used by the effect interpreter and kernel normalizer. Deep values should remain ordinary data, not a reason to avoid the generated layer.

That is why generated datatypes are suitable for infrastructure DSLs: they give the checker real structure without moving the user out of pure Nix evaluation.

What comes next

The next chapter shows what the metadata buys. Once a datatype carries a description, generic tools can derive schemas, dependency graphs, diagnostics, and ornaments without each tool inventing its own walker.