Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Protolens composition

A lens converts data between two particular schemas. A protolens records a schema transformation before it is instantiated at a particular source. Field renaming is a protolens; renaming age to years in one concrete record schema is the lens obtained by instantiation.

A protolens thus carries source and target TheoryEndofunctor descriptions rather than two concrete schemas.

Composition ordinarily requires the first target endofunctor to equal the second source endofunctor structurally. The implementation also accepts a second protolens whose source transform is Identity, retaining its precondition for later applicability checks. Neither branch proves naturality.

Lens DSL defines the concrete recipe being composed.

Semantic domain

The categorical interpretation treats a protolens as a natural transformation between schema endofunctors, using the standard definition introduced by Eilenberg and Mac Lane (Eilenberg and Mac Lane 1945):

where and are functors on the category of schemas , and for each schema , is a lens

satisfying the lens laws described in Lens DSL. The Rust type stores data from which a family of component lenses can be instantiated. It does not certify the lens laws for every component or naturality for every schema morphism.

The naturality condition is: for every schema morphism , the square

commutes: . Applying the protolens then transporting along gives the same result as transporting then applying the protolens.

Composition

Two protolenses and compose vertically into pointwise, by :

In the categorical interpretation, composition requires the intermediate functors to match. The implementation checks this requirement with protolens_composable, whose schematic form appears below. Omitted module paths and surrounding declarations make the excerpt non-runnable.

pub fn protolens_composable(eta: &Protolens, theta: &Protolens) -> bool {
    matches!(theta.source.transform, TheoryTransform::Identity)
        || theory_endofunctor_equiv(&eta.target, &theta.source)
}

A Protolens carries its source and target TheoryEndofunctors as public fields. theory_endofunctor_equiv compares preconditions and transforms while ignoring the human-readable name. When the source transform of is Identity, the predicate returns true regardless of ’s target.

vertical_compose enforces this predicate at construction time and returns the unit variant LensError::CompositionMismatch on failure. The error identifies the class of mismatch but does not carry an offending functor name.

Sequential and fused instantiation

For a nonempty chain, instantiate first fuses and instantiates the resulting protolens once. instantiate_sequential instead instantiates each step at the running schema and folds the resulting concrete lenses through compose. Both methods return one Lens, not a list of intermediate morphisms. The sequential route does not expose intermediate schemas through its return value, and its composed migration may lack metadata such as an expansion_path that fused construction computes globally. An empty chain instantiates to the identity lens, while fuse rejects an empty chain.

Tests compare the two routes on representative chains. They do not establish their equivalence for every chain.

Scope of the composition guard

Mathematically, if and are natural transformations, their pointwise composite is natural, and lawful component lenses compose to a lawful component lens. Structural endofunctor equivalence is the implementation’s evidence that the middle object matches in the ordinary case.

The identity-source branch is weaker. It accepts composition without establishing that ’s target equals ’s source as an endofunctor, then conjoins the second source precondition with the retained source precondition. Protolens::check_applicability and the chain APIs can reject a concrete schema that fails this retained obligation. protolens_composable is thus a construction guard, not a proof that every accepted composite is a natural transformation.

Vertical and horizontal composition have structural tests, but the implementation does not quantify over all schema morphisms to certify naturality. It also treats protolenses that compute the same transform through different intermediate forms as distinct. Tests compare fused and sequential instantiation on representative chains, while the two routes may preserve different metadata shapes.

See also