Documentation

Strata.DL.Lambda.Factory

Lambda's Factory #

This module formalizes Lambda's factory, which is a mechanism to extend the type checker (see Strata.DL.Lambda.LExprT) and partial evaluator (see Strata.DL.Lambda.LExprEval) by providing a map from operations to their types and optionally, denotations. The factory allows adding type checking and evaluation support for new operations without modifying the implementation of either or any core ASTs.

Also see Strata.DL.Lambda.IntBoolFactory for a concrete example of a factory.

@[reducible, inline]
abbrev Lambda.Signature (IDMeta Ty : Type) :

A signature is a map from variable identifiers to types.

Equations
Instances For
    @[reducible, inline]
    abbrev Lambda.LMonoTySignature {IDMeta : Type} :
    Equations
    Instances For
      @[reducible, inline]
      abbrev Lambda.LTySignature {IDMeta : Type} :
      Equations
      Instances For
        @[reducible, inline]

        A Lambda factory function - instantiation of Func for Lambda expressions.

        Universally quantified type identifiers, if any, appear before this signature and can quantify over the type identifiers in it.

        Equations
        Instances For
          def Lambda.LFunc.mk {T : LExprParams} (name : T.Identifier) (typeArgs : List TyIdentifier := []) (isConstr isRecursive : Bool := false) (inputs : ListMap T.Identifier LMonoTy) (output : LMonoTy) (body : Option (LExpr T.mono) := none) (attr : Array Strata.DL.Util.FuncAttr := #[]) (concreteEval : Option (T.MetadataList (LExpr T.mono)Option (LExpr T.mono)) := none) (axioms : List (LExpr T.mono) := []) (preconditions : List (Strata.DL.Util.FuncPrecondition (LExpr T.mono) T.Metadata) := []) (measure : Option (LExpr T.mono) := none) :

          Helper constructor for LFunc to maintain backward compatibility.

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

                    The type checker and partial evaluator for Lambda is parameterizable by a user-provided Factory.

                    We don't have any "built-in" functions like +, -, etc. in (LExpr IDMeta) -- lambdas are our only tool. Factory gives us a way to add support for concrete/symbolic evaluation and type checking for FunFactory functions without actually modifying any core logic or the ASTs.

                    Instances For
                      theorem Lambda.Factory.name_nodup {T : LExprParams} (f : Factory T) :
                      (List.map (fun (x : LFunc T) => x.name.name) f.toArray.toList).Nodup

                      The function names in a factory are unique.

                      def Lambda.Factory.mem {T : LExprParams} (f : Factory T) (name : String) :
                      Equations
                      Instances For
                        Equations
                        Instances For
                          @[implicit_reducible]
                          Equations
                          def Lambda.Factory.get {T : LExprParams} (f : Factory T) (name : String) (p : name f) :
                          Equations
                          Instances For
                            def Lambda.Factory.get? {T : LExprParams} (f : Factory T) (name : String) :
                            Equations
                            Instances For
                              Equations
                              Instances For
                                def Lambda.Factory.push {T : LExprParams} (F : Factory T) (fn : LFunc T) (is_new : ¬fn.name.name F) :
                                Equations
                                Instances For

                                  Insert fn into the factory if no function with the same name already exists.

                                  Equations
                                  Instances For
                                    theorem Lambda.Factory.ofArray_mem {T : LExprParams} {a : Array (LFunc T)} {fn : LFunc T} (p : fn (ofArray a).toArray) :
                                    fn a
                                    theorem Lambda.Factory.push_mem_iff {T : LExprParams} (f : Factory T) (fn : LFunc T) (h : ¬fn.name.name f) (name : String) :
                                    name f.push fn h name = fn.name.name name f
                                    theorem Lambda.Factory.mem_iff_mem_names {T : LExprParams} (f : Factory T) (s : String) :
                                    s f s Array.map (fun (x : LFunc T) => x.name.name) f.toArray
                                    theorem Lambda.Factory.push_mem_match {T : LExprParams} (f : Factory T) (fn : LFunc T) (h : ¬fn.name.name f) (name : String) :
                                    (f.push fn h)[name]? = if name = fn.name.name then some fn else f[name]?
                                    theorem Lambda.Factory.getElem?_is_some_implies_mem {T : LExprParams} {f : Factory T} {name : String} {fn : LFunc T} (eq : f[name]? = some fn) :
                                    theorem Lambda.Factory.getElem?_some_implies_mem {T : LExprParams} {f : Factory T} {name : String} {fn : LFunc T} (eq : f[name]? = some fn) :
                                    name f
                                    theorem Lambda.Factory.getElem?_some_getElem {T : LExprParams} {f : Factory T} {name : String} {fn : LFunc T} (eq : f[name]? = some fn) :
                                    f[name] = fn
                                    theorem Lambda.Factory.mem_name_eq_getElem {T : LExprParams} {F : Factory T} {fn : LFunc T} {s : String} (hmem : fn F.toArray) (hname : fn.name.name = s) :
                                    (hs : s F), F[s] = fn

                                    If fn ∈ F.toArray and fn.name.name = s, then s ∈ F and F[s] = fn.

                                    Add a function func to the factory F. Redefinitions are not allowed.

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

                                      Append a factory newF to an existing factory F, checking for redefinitions along the way.

                                      Equations
                                      Instances For

                                        Append a factory newF to an existing factory F, checking for redefinitions along the way.

                                        Equations
                                        Instances For
                                          def Lambda.getLFuncCall {T : LExprParams} {GenericTy : Type} (e : LExpr { base := T, TypeType := GenericTy }) :
                                          LExpr { base := T, TypeType := GenericTy } × List (LExpr { base := T, TypeType := GenericTy })
                                          Equations
                                          Instances For
                                            def Lambda.getLFuncCall.go {T : LExprParams} {GenericTy : Type} (e : LExpr { base := T, TypeType := GenericTy }) (acc : List (LExpr { base := T, TypeType := GenericTy })) :
                                            LExpr { base := T, TypeType := GenericTy } × List (LExpr { base := T, TypeType := GenericTy })
                                            Equations
                                            Instances For
                                              def Lambda.getConcreteLFuncCall {T : LExprParams} {GenericTy : Type} (e : LExpr { base := T, TypeType := GenericTy }) :
                                              LExpr { base := T, TypeType := GenericTy } × List (LExpr { base := T, TypeType := GenericTy })
                                              Equations
                                              Instances For
                                                def Lambda.Factory.callOfLFunc {T : LExprParams} {GenericTy : Type} (F : Factory T) (e : LExpr { base := T, TypeType := GenericTy }) (allowPartialApp : Bool := false) :
                                                Option (LExpr { base := T, TypeType := GenericTy } × List (LExpr { base := T, TypeType := GenericTy }) × LFunc T)

                                                If e is a call of a factory function, get the operator (.op), a list of all the actuals, and the (LFunc IDMeta).

                                                Equations
                                                • One or more equations did not get rendered due to their size.
                                                Instances For
                                                  theorem Lambda.callOfLFunc_eq_some {T : LExprParams} {GenericTy : Type} {F : Factory T} {e callee : LExpr { base := T, TypeType := GenericTy }} {args : List (LExpr { base := T, TypeType := GenericTy })} {fn : LFunc T} (hcall : F.callOfLFunc e = some (callee, args, fn)) :
                                                  (m : { base := T, TypeType := GenericTy }.base.Metadata), (name : Identifier { base := T, TypeType := GenericTy }.base.IDMeta), (ty : Option { base := T, TypeType := GenericTy }.TypeType), callee = LExpr.op m name ty F[name.name]? = some fn args.length = List.length fn.inputs
                                                  theorem Lambda.callOfLFunc_getLFuncCall {T : LExprParams} {GenericTy : Type} {F : Factory T} {e callee : LExpr { base := T, TypeType := GenericTy }} {args : List (LExpr { base := T, TypeType := GenericTy })} {fn : LFunc T} {aPA : Bool} (hcall : F.callOfLFunc e aPA = some (callee, args, fn)) :
                                                  getLFuncCall e = (callee, args)
                                                  theorem Lambda.getLFuncCall.go_size {T : LExprParamsT} {e : LExpr T} {op : LExpr { base := T.base, TypeType := T.TypeType }} {args acc : List (LExpr { base := T.base, TypeType := T.TypeType })} :
                                                  go e acc = (op, args)op.sizeOf + (List.map LExpr.sizeOf args).sum e.sizeOf + (List.map LExpr.sizeOf acc).sum
                                                  theorem Lambda.List.sum_size_le {α : Type u_1} (f : αNat) {l : List α} {x : α} (x_in : x l) :
                                                  f x (List.map f l).sum
                                                  theorem Lambda.getLFuncCall_smaller {T : LExprParamsT} {e : LExpr T} {op : LExpr { base := T.base, TypeType := T.TypeType }} {args : List (LExpr { base := T.base, TypeType := T.TypeType })} :
                                                  getLFuncCall e = (op, args)∀ (a : LExpr { base := T.base, TypeType := T.TypeType }), a argsa.sizeOf < e.sizeOf
                                                  theorem Lambda.Factory.callOfLFunc_smaller {T : LExprParamsT} {F : Factory T.base} {e : LExpr T} {op : LExpr { base := T.base, TypeType := T.TypeType }} {args : List (LExpr { base := T.base, TypeType := T.TypeType })} {F' : LFunc T.base} {allowPartialMatch : Bool} :
                                                  F.callOfLFunc e allowPartialMatch = some (op, args, F')∀ (a : LExpr { base := T.base, TypeType := T.TypeType }), a argsa.sizeOf < e.sizeOf
                                                  theorem Lambda.Factory.getElem?_name {T : LExprParams} {F : Factory T} {s : String} {fn : LFunc T} (h : F[s]? = some fn) :
                                                  fn.name.name = s

                                                  If F[s]? finds a function, its name matches the query.

                                                  theorem Lambda.Factory.callOfLFunc_arity {T : LExprParams} {F : Factory T} {e callee : LExpr T.mono} {args : List (LExpr T.mono)} {fn : LFunc T} (hcall : F.callOfLFunc e = some (callee, args, fn)) :

                                                  callOfLFunc ensures the number of args equals the number of inputs.

                                                  theorem Lambda.Factory.callOfLFunc_getElem? {T : LExprParams} {F : Factory T} {e callee : LExpr T.mono} {args : List (LExpr T.mono)} {fn : LFunc T} {aPA : Bool} (hcall : F.callOfLFunc e aPA = some (callee, args, fn)) :

                                                  The callee of callOfLFunc is an .op whose name resolves to fn via F[_]?.

                                                  theorem Lambda.callOfLFunc_func_mem {T : LExprParams} (F : Factory T) (e op : LExpr T.mono) (args : List (LExpr T.mono)) (func : LFunc T) (aPA : Bool) (h : F.callOfLFunc e aPA = some (op, args, func)) :
                                                  func F.toArray

                                                  If callOfLFunc returns a triple, the function is a member of the factory array.

                                                  Apply type substitution S to all type annotations in an LExpr. This is only for user-defined types, not metadata-stored resolved types. If e is an LExprT whose metadata contains type information, use applySubstT.

                                                  Equations
                                                  Instances For

                                                    Best-effort type extraction from an LExpr without a typing context. Returns none when the type cannot be determined syntactically.

                                                    Equations
                                                    Instances For

                                                      Derive a type substitution from the .op type annotation alone, by unifying it against the function's generic type. On annotated terms (i.e., terms that have undergone type inference), the .op node always carries a type annotation, so this suffices.

                                                      Returns some Subst.empty when fn.typeArgs is empty (monomorphic — no-op). Returns none if the callee is not annotated or unification fails.

                                                      Equations
                                                      Instances For
                                                        def Lambda.LFunc.computeTypeSubst {T : LExprParams} (fn : LFunc T) (callee : LExpr T.mono) (args : List (LExpr T.mono)) :

                                                        Derive a type substitution by unifying the instantiated operator type against the function's generic type. Used when inlining a polymorphic function body to instantiate type variables.

                                                        Prefers the .op annotation (via opTypeSubst). Falls back to a best-effort approach using argument types when the .op is not annotated. On annotated terms (after type inference), the .op always carries a type annotation, so the fallback is never needed.

                                                        Returns some Subst.empty when fn.typeArgs is empty (monomorphic — no-op). Returns none if the type substitution cannot be derived.

                                                        Equations
                                                        • One or more equations did not get rendered due to their size.
                                                        Instances For
                                                          theorem Lambda.LFunc.computeTypeSubst_of_opTypeSubst {T : LExprParams} {fn : LFunc T} {callee : LExpr T.mono} {args : List (LExpr T.mono)} {s : Subst} (h : fn.opTypeSubst callee = some s) :
                                                          fn.computeTypeSubst callee args = some s

                                                          When opTypeSubst succeeds, computeTypeSubst agrees with it.