Documentation

Transfer.Deriving.ParamDeriveHandler

The description-based @[derive Param] handler #

ParamDerive.lean specified the deriving-handler algorithm (its 6-step design note) and hand-ported the representative inductive shapes (R_sum/paramSum, R_nat/paramNat); ParamData.lean hand-ported R_prod/R_option/R_list. This file turns the front half of that spec into a running elaborator: an IndDesc extraction in AdapTT's style, a mixed-variance guard, and a generator that emits the relational lift R_<T> of a simple inductive uniformly from its constructor signatures — the constructor-wise Forall₂-shape congruence the hand-ports reproduce by hand.

Implemented vs. specified #

Running elaborator (this file, exercised by the demos below):

  1. IndDesc / CtorDesc / FieldKind — AdapTT's IndDesc scheme: a constructor is a list of fields, each field classified as
    • param i — a non-recursive covariant occurrence of the i-th type parameter (e.g. the a in Tree.leaf a),
    • recursive — the inductive itself applied to the params (e.g. the subtrees in Tree.node),
    • other — a parameter-free field type, lifted by equality.
  2. Param.occPol — a polarity (variance) analysis of a parameter's occurrences in a field type, flipping polarity under each arrow domain.
  3. Param.toIndDesc : Name → MetaM IndDesc — reads an InductiveVal via getConstInfoInduct, walks each constructor's field telescope (forallTelescopeReducing), and classifies every field. Here the mixed-variance guard applies: a parameter that occurs in both co- and contravariant position (the canonical non-example, e.g. a field of type A → A) is rejected with AdapTT's NonExample (Mixed variance); a purely contravariant occurrence is likewise rejected (a Param lift of A is a relation A → A' → Prop, which only supplies a covariant element relation).
  4. Param.mkRelInductive : IndDesc → Name → CommandElabM (TSyntax \command)— the generator: emits `inductive R_<T> {A₀ A₀' …} (P₀ : A₀ → A₀' → Prop) … : T A₀ … → T A₀' … → Prop with one constructor per source constructor, each taking the per-field relatedness hypotheses (Pᵢ aⱼ aⱼ'for a param field; the recursiveR_<T> …for a recursive field;aⱼ = aⱼ'for anotherfield) and concluding `R_<T> P… (c a…) (c a'…). This is exactly the shape of R_sum/R_list` etc.
  5. Param.mkParamMap1Cmds : IndDesc → Name → … — for a non-recursive inductive (records / enums — the crypto carriers), the relation is upgraded to a full annotated Param: it emits the forward map T.paramMap (constructor-wise, applying each parameter's forward map to the matching field) and the instance T.param_instance : Param .map3 .map0 (T A…) (T A'…) whose R is the generated R_<T>. The map_in_R and R_in_map proof fields are synthesized constructor-wise (cases + exact R_<T>.<c> … / simp [T.paramMap, …]) — a finite per-constructor term, no induction needed because no field is the inductive itself. Element inputs are taken at Param .map3 .map3 (the level supplying map/map_in_R/R_in_map, e.g. paramId's level). This is exactly the shape paramProd/paramOption reproduce by hand, at the forward-map3 level so the result feeds castViaParam.
  6. Param.isUniformRecursive / Param.mkParamMapRecCmds — for a uniform-recursive inductive (List/Tree-shape: every recursive field is T applied to the same parameters), the same full Param .map3 .map0 instance is generated, but the three forward fields are recursive: T.paramMap by structural recursion (recurse on recursive fields), map_in_R by induction on the value, R_in_map by induction on the R_<T> derivation — recursive arms use the induction hypothesis, param arms the element Pⱼ.fwd.*. isUniformRecursive gates the path: non-uniform recursion (nesting / arrows / differing args / indices) stays relation-only.
  7. Param.mkParamDeriveCmds / mkParamInstanceHandler — registered as the @[derive Param] deriving handler (registerDerivingHandler): on deriving instance Param for T (or a deriving Param clause) it elaborates the generated R_<T> relation and the T.paramMap / T.param_instance (non-recursive or uniform-recursive) into the environment.

What the map-instance synthesis covers, and the boundary:

So: the @[derive Param] attribute is registered and runs; both the non-recursive and the uniform-recursive path generate the relation and the forward map3 Param instance with the variance guard; non-uniform recursive map/proof synthesis is the labelled residual.

IndDesc: AdapTT's description of an inductive's constructor signatures #

How a constructor field is lifted by the parametricity translation.

  • param i: a non-recursive covariant occurrence of the i-th type parameter — lifted by the supplied element relation Pᵢ.
  • recursive: the inductive applied to the params — lifted by the recursive relation R_<T>.
  • other: a parameter-free field type — lifted by equality.
Instances For
    Equations
    • One or more equations did not get rendered due to their size.
    Instances For
      Equations
      Instances For

        A constructor: its name and the lift-classification of each field.

        Instances For

          A simple inductive's description: name, parameter count, constructors. The IndDesc of AdapTT's description-based scheme — the generator reads only this.

          Instances For

            Variance analysis and the mixed-variance guard #

            partial def Transfer.Param.occPol (p : Lean.FVarId) (pos : Bool) (e : Lean.Expr) :

            Polarity (variance) of a parameter p's occurrences in e. Returns (occursPositively, occursNegatively); the domain of an arrow flips polarity. A parameter that comes back (true, true) is mixed variance — AdapTT's NonExample.

            Is e the head selfName applied to exactly the type parameters, in order (T p₀ … p_{k-1})? The uniform-recursion shape, reused to recognise the List-nested occurrence.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For

              Classify a single constructor field type. Rejects mixed- and purely contravariant parameter occurrences (the Param element relation is covariant). A bare covariant occurrence A becomes param i; the inductive applied to the params becomes recursive; List (T p…) becomes nestedList; a parameter-free type is other.

              Equations
              • One or more equations did not get rendered due to their size.
              Instances For

                Read a simple inductive into an IndDesc, classifying every constructor field — the MetaM extraction. The mixed-variance guard applies here.

                Equations
                • One or more equations did not get rendered due to their size.
                Instances For
                  partial def Transfer.Param.mentions (selfName : Lean.Name) (e : Lean.Expr) :

                  Does e mention the constant selfName anywhere? Used to detect a recursive occurrence nested under another type former (e.g. List (T A)), which the uniform map generator does not handle.

                  Is T's recursion uniform (List/Tree-shape)? Every constructor field that mentions T must be T applied to exactly the type parameters, in order — a direct recursive sub-term. Fields with T nested under another type former (List (T A)), applied to different arguments, or under an arrow (higher-order recursion) make recursion non-uniform: the structural forward map / its proofs are out of scope, so the generator keeps such inductives relation-only.

                  Equations
                  • One or more equations did not get rendered due to their size.
                  Instances For

                    The relation generator #

                    Generate the relational lift R_<T> of an inductive uniformly from its IndDesc: a fresh inductive relation with one constructor per source constructor, the constructor-wise Forall₂-shape congruence. This is the shape R_sum/R_prod/R_option/R_list reproduce by hand.

                    Equations
                    • One or more equations did not get rendered due to their size.
                    Instances For

                      The relation name the handler emits for inductive T: …T.R_param.

                      Equations
                      Instances For

                        The forward-map name the handler emits for inductive T: …T.paramMap.

                        Equations
                        Instances For

                          The Param instance name the handler emits for inductive T: …T.param_instance.

                          Equations
                          Instances For

                            The Param .map3 instance generator (non-recursive inductives) #

                            For a non-recursive inductive T (records / enums — the crypto carriers) the relation R_<T> generated above is upgraded to a full annotated relation: a forward map plus both graph inclusions (map_in_R, R_in_map). All three are finite, per-constructor terms — no induction is needed because no field is the inductive itself — so the handler synthesizes them directly, mirroring the hand-written paramProd/paramOption/paramList of ParamData (which build the same constructor-wise shape) but at the map3 forward level (so the result is a usable Param, e.g. it feeds castViaParam).

                            Element inputs are taken at Param .map3 .map3 (the level supplying map, map_in_R, R_in_map — paramId's level), and the synthesized instance is Param .map3 .map0: forward map3, backward map0 (the relation alone backward, as for the data combinators).

                            Generate, for a non-recursive inductive T, the forward map T.paramMap and the Param .map3 .map0 instance T.param_instance whose relation is the already-generated R_<T>. The per-field proofs are finite (no induction) because no field is the inductive itself. Recursive inductives are routed to mkParamMapRecCmds instead; the defensive guard below returns #[] should a recursive field reach here.

                            Equations
                            • One or more equations did not get rendered due to their size.
                            Instances For

                              The Param .map3 instance generator (uniform-recursive inductives) #

                              For a uniform-recursive inductive T (List/Tree-shape: every recursive field is T applied to the same parameters, no nesting / arrows / higher-order recursion — isUniformRecursive), the relation R_<T> is upgraded to the same full Param .map3 .map0 annotated relation as the non-recursive path, but the three forward fields are recursive:

                              This reproduces the hand-written paramList/paramOption of ParamData generically, at the full map3 forward level (so the result feeds castViaParam). Element inputs are taken at Param .map3 .map3 (as for the non-recursive path / paramId).

                              Generate, for a uniform-recursive inductive T, the structurally recursive forward map T.paramMap and the Param .map3 .map0 instance T.param_instance whose relation is the already-generated R_<T>. Assumes isUniformRecursive held (otherwise returns #[], relation-only).

                              Equations
                              • One or more equations did not get rendered due to their size.
                              Instances For

                                The Param .map3 instance generator (nested-List-recursive inductives) #

                                Extension 1. For an inductive T with a List (T p…) field (the Rose-shape), the forward map recurses through List.map, and the two graph inclusions are proved by mutual structural recursion with a List helper — the induction tactic rejects nested inductives, but the equation compiler accepts the mutual recursion. The relation R_<T> (from mkRelInductive) already lifts the nested field by List.Forall₂. List is the canonical functorial container; Option / Array are the same shape (a registered map + relation lifter + congruence).

                                Does T have a List (T p…) field (the nested-List-recursive shape that mkParamMapNestedCmds handles)?

                                Equations
                                Instances For

                                  Generate, for a nested-List-recursive inductive T, the forward map T.paramMap (structural recursion through List.map) and the full Param .map3 .map0 instance, with map_in_R / R_in_map proved by mutual structural recursion + a List helper. Relation is the generated R_<T>.

                                  Equations
                                  • One or more equations did not get rendered due to their size.
                                  Instances For

                                    Commands the @[derive Param] handler emits for one inductive: the relational lift R_<T> (the constructor-wise congruence) and the forward map T.paramMap + Param .map3 .map0 instance T.param_instance. The non-recursive path (mkParamMap1Cmds), the uniform-recursive path (mkParamMapRecCmds), and the nested-List-recursive path (mkParamMapNestedCmds) all reach the full map3 forward instance. Other non-uniform recursion (other-container nesting / arrows / indexed) stays relation-only.

                                    Equations
                                    • One or more equations did not get rendered due to their size.
                                    Instances For

                                      Elaborate a generated command at the root namespace. The generated relation carries its fully-qualified name (<T>.R_param), so it must be declared with the current namespace cleared — otherwise an open namespace would be prepended a second time. (Deriving handlers proper already run at root; this makes the generator namespace-robust for the in-file demo too.)

                                      Equations
                                      • One or more equations did not get rendered due to their size.
                                      Instances For

                                        Registration as the @[derive Param] deriving handler #

                                        The deriving handler for Param. Generates, for each named simple inductive, its relational lift R_<T> uniformly from the constructor signatures, with the mixed-variance guard. Returns false (unhandled) for inductives the uniform generator does not cover (nested / reflexive / indexed families), so other handlers / a manual derivation can take over.

                                        Equations
                                        • One or more equations did not get rendered due to their size.
                                        Instances For

                                          Demos #

                                          Lean's initialize blocks take effect only after the enclosing module is imported, so the registerDerivingHandler above is live for downstream files (deriving Param / deriving instance Param for T there route to mkParamInstanceHandler) but not within this module. To exercise the generator here it is invoked directly via run_cmd — the same MetaM/command elaboration the registered handler runs — defining the test inductives first and then emitting their relational lift R_param.

                                          inductive Transfer.Param.Pair (A B : Type) :

                                          A binary product-like inductive (two covariant parameters).

                                          Instances For
                                            inductive Transfer.Param.Tree (A : Type) :

                                            A recursive inductive (one covariant parameter + recursive subterms).

                                            Instances For

                                              A parameter-free enum (the base case: relation collapses to a tag match).

                                              Instances For
                                                inductive Transfer.Param.Tree.R_param {A0 A0p : Type} (P0 : A0 → A0p → Prop) :
                                                Tree A0 → Tree A0p → Prop
                                                Instances For
                                                  inductive Transfer.Param.Pair.R_param {A0 A1 A0p A1p : Type} (P0 : A0 → A0p → Prop) (P1 : A1 → A1p → Prop) :
                                                  Pair A0 A1 → Pair A0p A1p → Prop
                                                  • mk {A0 A1 A0p A1p : Type} {P0 : A0 → A0p → Prop} {P1 : A1 → A1p → Prop} {x0 : A0} {x1 : A1} {x0p : A0p} {x1p : A1p} : P0 x0 x0p → P1 x1 x1p → R_param P0 P1 (Pair.mk x0 x1) (Pair.mk x0p x1p)
                                                  Instances For
                                                    @[reducible]
                                                    Equations
                                                    Instances For
                                                      @[reducible]
                                                      Equations
                                                      • One or more equations did not get rendered due to their size.
                                                      Instances For
                                                        def Transfer.Param.Pair.paramMap {A0 A1 A0p A1p : Type} (P0 : Param MapClass.map3 MapClass.map3 A0 A0p) (P1 : Param MapClass.map3 MapClass.map3 A1 A1p) :
                                                        Pair A0 A1 → Pair A0p A1p
                                                        Equations
                                                        Instances For
                                                          @[reducible]
                                                          Equations
                                                          Instances For

                                                            Recursive instance: Tree and MyList get the full map3 instance #

                                                            The run_cmd above ran the generator on Tree, which isUniformRecursive accepts (the node subtrees are Tree A — the inductive at the same param), so the uniform-recursive path applies and emits Tree.paramMap + Tree.param_instance. A cons-shaped MyList (a param and a recursive field in one constructor) exercises the mixed-field arm.

                                                            A cons-list-shaped recursive inductive (one param + recursive tail).

                                                            Instances For
                                                              @[reducible]
                                                              Equations
                                                              Instances For
                                                                inductive Transfer.Param.MyList.R_param {A0 A0p : Type} (P0 : A0 → A0p → Prop) :
                                                                MyList A0 → MyList A0p → Prop
                                                                Instances For

                                                                  Nested-List recursive instance: RoseT gets the FULL map3 instance (Extension 1) #

                                                                  A Rose-shape inductive — a recursive occurrence nested under List — is routed by isNestedListSupported to mkParamMapNestedCmds. Its forward map recurses through List.map, and the two graph inclusions are proved by mutual structural recursion with a List helper (the induction tactic rejects nested inductives; the equation compiler accepts the mutual recursion).

                                                                  inductive Transfer.Param.RoseT (A : Type) :

                                                                  A rose tree: children nested under List (the canonical functorial container).

                                                                  Instances For
                                                                    @[reducible]
                                                                    Equations
                                                                    Instances For
                                                                      theorem Transfer.Param.RoseT.param_mapInR_list {A0 A0p : Type} (P0 : Param MapClass.map3 MapClass.map3 A0 A0p) (xs✝ : List (RoseT A0)) :
                                                                      List.Forall₂ (R_param P0.R) xs✝ (List.map (paramMap P0) xs✝)
                                                                      inductive Transfer.Param.RoseT.R_param {A0 A0p : Type} (P0 : A0 → A0p → Prop) :
                                                                      RoseT A0 → RoseT A0p → Prop
                                                                      Instances For
                                                                        theorem Transfer.Param.RoseT.param_RinMap {A0 A0p : Type} (P0 : Param MapClass.map3 MapClass.map3 A0 A0p) {x✝ : RoseT A0} {y✝ : RoseT A0p} :
                                                                        R_param P0.R x✝ y✝ → paramMap P0 x✝ = y✝
                                                                        theorem Transfer.Param.RoseT.param_mapInR {A0 A0p : Type} (P0 : Param MapClass.map3 MapClass.map3 A0 A0p) (x✝ : RoseT A0) :
                                                                        R_param P0.R x✝ (paramMap P0 x✝)
                                                                        theorem Transfer.Param.RoseT.param_RinMap_list {A0 A0p : Type} (P0 : Param MapClass.map3 MapClass.map3 A0 A0p) {xs✝ : List (RoseT A0)} {ys✝ : List (RoseT A0p)} :
                                                                        List.Forall₂ (R_param P0.R) xs✝ ys✝ → List.map (paramMap P0) xs✝ = ys✝

                                                                        Crypto-record demos: a derived Param .map3 .map0 instance in use #

                                                                        A @[derive Param] on a non-recursive crypto carrier (an enum like a participant Role, a record like an ElGamal ciphertext or a signature pair) yields a usable annotated relation — a forward map (via castViaParam) and the proved graph inclusion map_in_R — not just R_<T>.

                                                                        A participant role (parameter-free enum).

                                                                        Instances For

                                                                          An ElGamal ciphertext over a group carrier G: a pair of group elements.

                                                                          Instances For

                                                                            A signature pair: a group element plus a (parameter-free) scalar tag.

                                                                            Instances For
                                                                              inductive Transfer.Param.ElGamalCt.R_param {A0 A0p : Type} (P0 : A0 → A0p → Prop) :
                                                                              ElGamalCt A0 → ElGamalCt A0p → Prop
                                                                              Instances For
                                                                                @[reducible]
                                                                                Equations
                                                                                Instances For
                                                                                  @[reducible]
                                                                                  Equations
                                                                                  Instances For
                                                                                    @[reducible]
                                                                                    Equations
                                                                                    • One or more equations did not get rendered due to their size.
                                                                                    Instances For
                                                                                      inductive Transfer.Param.SigPair.R_param {A0 A0p : Type} (P0 : A0 → A0p → Prop) :
                                                                                      SigPair A0 → SigPair A0p → Prop
                                                                                      Instances For

                                                                                        The mixed-variance non-example is rejected #

                                                                                        toIndDesc (hence the @[derive Param] handler) rejects an inductive whose parameter occurs in both co- and contravariant position — AdapTT's NonExample (Mixed variance).

                                                                                        inductive Transfer.Param.Endo (A : Type) :

                                                                                        A field of type A → A makes A mixed-variance.

                                                                                        Instances For

                                                                                          The non-uniform-recursion boundary #

                                                                                          A recursive occurrence nested under another type former (List (Rose A)), applied to different arguments, under an arrow, or in an indexed family is non-uniform: the structural forward map / its proofs are out of scope. isUniformRecursive is the gate — it returns false, so the uniform-recursive map path does not apply. (For nested recursion such as Rose below the relation generator itself does not apply either — its other-by-equality lift is ill-typed across the two parameter instantiations, and the registered handler declines such inductives up front via iv.isNested. The labelled residual is thus: only the uniform List/Tree shape reaches the full map3 instance.)

                                                                                          inductive Transfer.Param.Rose (A : Type) :

                                                                                          A rose tree: the children are a List (Rose A) — recursion nested under List, hence non-uniform. Used only to exercise the isUniformRecursive classifier; the generator is not run on it.

                                                                                          Instances For