Lens semantics and laws

A lens is a structured pair of functions between two schemas:

get translates a source value A into a target view B plus a complement (the data the projection discarded). put takes a (possibly modified) B and the complement, and reconstructs an A.

This shape is panproto's state-based asymmetric lens and is what idiolect-lens runs on the wire.

The laws

A well-formed lens obeys two laws.

GetPut

Restoring the complement recovers the original.

In runtime form: if you apply_lens a record forward and then apply_lens_put it back, you get the source bytes you started with.

PutGet

Lifting then projecting gives back the modified view.

In runtime form: if you write a target view through put and then read it back through get, the view and complement match what you wrote.

Iso laws

When the lens is an isomorphism (no information dropped), the complement is empty and the laws collapse:

The two directions are total inverses.

Optic classification

panproto's classifier assigns each lens chain one of five classes, based on what the chain promises:

ClassWhat it promisesWhat it allows
IsoBijective; both directions are total inverses.Auto-merge under the lexicon-evolution policy.
InjectionSource embeds in target without loss. Forward is total; backward needs no complement.Auto-merge as forward-only.
ProjectionTarget is a quotient of source; forward drops information. Backward needs the complement.PR review under the policy.
AffinePartial. The forward direction may fail on some inputs.PR review plus a community recommendation.
GeneralNone of the above.Manual lens authoring, full coercion-law check, plus verification.

The class is not a quality judgment; it is a routing decision. Some legitimate migrations are projections (a field genuinely went away). The policy makes the consequences visible.

Composition

Chain composition is associative:

Identity is the no-op lens; it is a left and right identity for composition. panproto's protolens runtime auto-simplifies adjacent steps where it can (RenameVertex(a,b) ; RenameVertex(b,c) → RenameVertex(a,c)).

Symmetric lenses

A symmetric lens pairs two state-based lenses that share a middle schema:

Sync from to goes , threading the complement through both halves. The dual sync uses the same machinery in reverse. apply_lens_symmetric runs either direction.

This is the right shape for bridging two communities' lexicons: each community owns its own lens to a shared middle schema, and the bridge stays consistent up to complement.

Coercion honesty

Lens chains that cross primitive kinds (Int↔Str, Float↔Int, ...) declare a CoercionClass per kind crossing:

CoercionClassWhen
IsoForward and inverse are total inverses (e.g. Int to its decimal string and back).
RetractionForward is total; inverse recovers the forward image only.
ProjectionForward drops information (e.g. Float to Int by truncation).
OpaqueDocumentation pair; no round-trip promise.

A dishonest Iso declaration silently corrupts the GetPut law. panproto ships a sample-based law checker (schema theory check-coercion-laws) that catches violations. The checker is wired into the lexicon-evolution gate.

What you have to verify

Stating the laws is cheap. Verifying them on a corpus is the work. The shipped runner kinds:

  • roundtrip-test runs GetPut on a corpus.
  • property-test runs an arbitrary boolean predicate.
  • static-check runs the panproto-level coercion-law and existence checks against the chain itself.

A lens with no published verifications is a claim; a lens with multiple published verifications from trusted signers, run on recent corpora, is closer to an asserted fact. See Author a verification runner for the authoring path.