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 concrete evaluator for a factory function (see LFunc.concreteEval). It takes the metadata to attach to the result, the type annotation of the function's operator at the call (none when the operator is not annotated), and the arguments of the call. It returns the result of the call, or none when it does not apply (e.g. the arguments are not literals). The operator's type gives the evaluator the instantiation of the function's type parameters, which it needs to build a literal of a type that is not determined by the arguments (e.g. the empty sequence).

        The soundness obligations on an evaluator (LFunc.InterpConsistentEval, Factory.EvalWellTyped, LFunc.EvalSound) constrain its result only when the annotation is the function's type instantiated by the call's type substitution (LFunc.instTy), which is the annotation of the operator of a well-typed call. No theorem covers its result at other annotations: none, which LExpr.eval passes for an unannotated operator and Core's model (gatherEval) always passes, or a type with type variables. So an evaluator that reads the annotation returns none unless the annotation is a ground type; an evaluator that ignores the annotation is covered at every annotation.

        Equations
        Instances For

          The type annotation of an operator, and none for any other expression.

          Equations
          Instances For
            @[reducible, inline]

            The AST-facing Lambda function structure - instantiation of the base Func for Lambda expressions. It is used for functions that appear in the Strata AST (e.g. Core.Function), and carries only plain data — the base Func excludes the function-typed concreteEval, so it has decidable equality.

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

            Equations
            Instances For

              A Lambda factory function - the full, evaluator/factory-facing function structure. It extends the AST-facing Func with the partial-evaluator hook concreteEval. All other fields live on the base Func.

              A optional evaluation function can be provided in the concreteEval field for each factory function to allow the partial evaluator to do constant propagation when all the arguments of a function are concrete; its type is ConcreteEval. Here's an example of a concreteEval function for Int.Add:

              (fun md _ args => match args with
                | [e1, e2] =>
                  match LExpr.denoteInt e1, LExpr.denoteInt e2 with
                  | some x, some y => some (.intConst md (x + y))
                  | _, _ => none
                | _ => none)
              

              Note that if there is an arity mismatch or if the arguments are not concrete/constants, this fails and it returns .none. If LFunc already has body, it must not have concreteEval, and vice versa.

              Instances For

                The type of the function f instantiated by the type substitution S: the arrow from its input types to its output type, each under S. The operator of a call of f well typed at the instance S is annotated with it, and the soundness obligations on concrete evaluators are stated at it (see ConcreteEval).

                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 (ConcreteEval T) := 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

                    Lift an AST-facing LFuncDefined (base Func) into the full LFunc, optionally attaching concreteEval at the evaluator boundary. All other data carries over unchanged via toFunc.

                    Equations
                    • f.toLFunc concreteEval = { toFunc := f, concreteEval := concreteEval }
                    Instances For
                      @[instance_reducible]
                      Equations
                      Equations
                      • One or more equations did not get rendered due to their size.
                      Instances For
                        Equations
                        Instances For
                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For

                            Every expression f carries: its body, its axioms, its preconditions and its measure. The LFunc fields it reads are Func's, so this is that function's list.

                            Equations
                            Instances For
                              Equations
                              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
                                  def Lambda.Factory.mem {T : LExprParams} (f : Factory T) (name : String) :
                                  Equations
                                  Instances For
                                    Equations
                                    Instances For
                                      @[instance_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
                                                def Lambda.Factory.map {T : LExprParams} (F : Factory T) (g : LFunc T → LFunc T) (hg : ∀ (f : LFunc T), (g f).name.name = f.name.name) :

                                                F with every function f replaced by g f, for a g that keeps function names.

                                                Equations
                                                Instances For

                                                  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