doc: Expr.lean

This commit is contained in:
E.W.Ayers 2022-03-24 14:22:03 -04:00 committed by Leonardo de Moura
parent 8f83c7ab32
commit 24ebd78071

View file

@ -30,6 +30,31 @@ instance : LT Literal := ⟨fun a b => a.lt b⟩
instance (a b : Literal) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.lt b))
/--
Arguments in forallE binders can be labelled as implicit or explicit.
Each `lam` or `forallE` binder comes with a `binderInfo` argument (stored in ExprData).
This can be set to
- `default` -- `(x : α)`
- `implicit` -- `{x : α}`
- `strict_implicit` -- `⦃x : α⦄`
- `inst_implicit` -- `[x : α]`.
- `aux_decl` -- Auxillary definitions are helper methods that
Lean generates. `aux_decl` is used for `_match`, `_fun_match`,
`_let_match` and the self reference that appears in recursive pattern matching.
The difference between implicit `{}` and strict-implicit `⦃⦄` is how
implicit arguments are treated that are *not* followed by explicit arguments.
`{}` arguments are applied eagerly, while `⦃⦄` arguments are left partially applied:
```lean
def foo {x : Nat} : Nat := x
def bar ⦃x : Nat⦄ : Nat := x
#check foo -- foo : Nat
#check bar -- bar : ⦃x : Nat⦄ → Nat
```
See also the Lean manual: https://leanprover.github.io/lean4/doc/expressions.html#implicit-arguments
-/
inductive BinderInfo where
| default | implicit | strictImplicit | instImplicit | auxDecl
deriving Inhabited, BEq, Repr
@ -65,20 +90,22 @@ def BinderInfo.isAuxDecl : BinderInfo → Bool
| BinderInfo.auxDecl => true
| _ => false
/-- Expression metadata. Used with the `Expr.mdata` constructor. -/
abbrev MData := KVMap
abbrev MData.empty : MData := {}
/--
Cached hash code, cached results, and other data for `Expr`.
hash : 32-bits
hasFVar : 1-bit
hasExprMVar : 1-bit
hasLevelMVar : 1-bit
hasLevelParam : 1-bit
nonDepLet : 1-bit
binderInfo : 3-bits
hasFVar : 1-bit -- does it contain free variables?
hasExprMVar : 1-bit -- does it contain metavariables?
hasLevelMVar : 1-bit -- does it contain level metavariables?
hasLevelParam : 1-bit -- does it contain level parameters?
nonDepLet : 1-bit -- internal flag, for tracking whether let x := v; b is equivalent to (fun x => b) v
binderInfo : 3-bits -- encoding of BinderInfo
approxDepth : 8-bits -- the approximate depth is used to minimize the number of hash collisions
looseBVarRange : 16-bits -/
looseBVarRange : 16-bits
-/
def Expr.Data := UInt64
instance: Inhabited Expr.Data :=
@ -268,6 +295,7 @@ def hasExprMVar (e : Expr) : Bool :=
def hasLevelMVar (e : Expr) : Bool :=
e.data.hasLevelMVar
/-- Does the expression contain level or expression metavariables?-/
def hasMVar (e : Expr) : Bool :=
let d := e.data
d.hasExprMVar || d.hasLevelMVar
@ -278,6 +306,11 @@ def hasLevelParam (e : Expr) : Bool :=
def approxDepth (e : Expr) : UInt32 :=
e.data.approxDepth.toUInt32
/-- The range of de-Bruijn variables that are loose.
That is, bvars that are not bound by a binder.
For example, `bvar i` has range `i + 1` and
an expression with no loose bvars has range `0`.
-/
def looseBVarRange (e : Expr) : Nat :=
e.data.looseBVarRange.toNat
@ -353,11 +386,11 @@ def mkForall (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) : Expr :=
(t.hasLevelParam || b.hasLevelParam)
bi
/- Return `Unit -> type`. Do not confuse with `Thunk type` -/
/-- Return `Unit -> type`. Do not confuse with `Thunk type` -/
def mkSimpleThunkType (type : Expr) : Expr :=
mkForall Name.anonymous BinderInfo.default (Lean.mkConst `Unit) type
/- Return `fun (_ : Unit), e` -/
/-- Return `fun (_ : Unit), e` -/
def mkSimpleThunk (type : Expr) : Expr :=
mkLambda `_ BinderInfo.default (Lean.mkConst `Unit) type
@ -410,6 +443,7 @@ def mkStrLit (s : String) : Expr :=
@[export lean_expr_mk_mdata] def mkMDataEx : MData → Expr → Expr := mkMData
@[export lean_expr_mk_proj] def mkProjEx : Name → Nat → Expr → Expr := mkProj
/-- `mkAppN f #[a₀, ..., aₙ]` ==> `f a₀ a₁ .. aₙ`-/
def mkAppN (f : Expr) (args : Array Expr) : Expr :=
args.foldl mkApp f
@ -420,6 +454,7 @@ private partial def mkAppRangeAux (n : Nat) (args : Array Expr) (i : Nat) (e : E
def mkAppRange (f : Expr) (i j : Nat) (args : Array Expr) : Expr :=
mkAppRangeAux j args i f
/-- Same as `mkApp f args` but reversing `args`. -/
def mkAppRev (fn : Expr) (revArgs : Array Expr) : Expr :=
revArgs.foldr (fun a r => mkApp r a) fn
@ -434,7 +469,7 @@ constant quickLt (a : @& Expr) (b : @& Expr) : Bool
@[extern "lean_expr_lt"]
constant lt (a : @& Expr) (b : @& Expr) : Bool
/- Return true iff `a` and `b` are alpha equivalent.
/-- Return true iff `a` and `b` are alpha equivalent.
Binder annotations are ignored. -/
@[extern "lean_expr_eqv"]
constant eqv (a : @& Expr) (b : @& Expr) : Bool
@ -519,6 +554,9 @@ def getForallBody : Expr → Expr
| forallE _ _ b .. => getForallBody b
| e => e
/-- If the given expression is a sequence of
function applications `f a₁ .. aₙ`, return `f`.
Otherwise return the input expression. -/
def getAppFn : Expr → Expr
| app f a _ => getAppFn f
| e => e
@ -527,6 +565,7 @@ def getAppNumArgsAux : Expr → Nat → Nat
| app f a _, n => getAppNumArgsAux f (n+1)
| e, n => n
/-- Counts the number `n` of arguments for an expression `f a₁ .. aₙ`. -/
def getAppNumArgs (e : Expr) : Nat :=
getAppNumArgsAux e 0
@ -534,6 +573,7 @@ private def getAppArgsAux : Expr → Array Expr → Nat → Array Expr
| app f a _, as, i => getAppArgsAux f (as.set! i a) (i-1)
| _, as, _ => as
/-- Given `f a₁ a₂ ... aₙ`, returns `#[a₁, ..., aₙ]` -/
@[inline] def getAppArgs (e : Expr) : Array Expr :=
let dummy := mkSort levelZero
let nargs := e.getAppNumArgs
@ -543,6 +583,7 @@ private def getAppRevArgsAux : Expr → Array Expr → Array Expr
| app f a _, as => getAppRevArgsAux f (as.push a)
| _, as => as
/-- Same as `getAppArgs` but reverse the output array. -/
@[inline] def getAppRevArgs (e : Expr) : Array Expr :=
getAppRevArgsAux e (Array.mkEmpty e.getAppNumArgs)
@ -550,6 +591,7 @@ private def getAppRevArgsAux : Expr → Array Expr → Array Expr
| app f a _, as, i => withAppAux k f (as.set! i a) (i-1)
| f, as, i => k f as
/-- Given `e = f a₁ a₂ ... aₙ`, returns `k f #[a₁, ..., aₙ]`. -/
@[inline] def withApp (e : Expr) (k : Expr → Array Expr → α) : α :=
let dummy := mkSort levelZero
let nargs := e.getAppNumArgs
@ -559,6 +601,7 @@ private def getAppRevArgsAux : Expr → Array Expr → Array Expr
| app f a _, as => withAppRevAux k f (as.push a)
| f, as => k f as
/-- Same as `withApp` but with arguments reversed. -/
@[inline] def withAppRev (e : Expr) (k : Expr → Array Expr → α) : α :=
withAppRevAux k e (Array.mkEmpty e.getAppNumArgs)
@ -572,17 +615,22 @@ def getRevArg! : Expr → Nat → Expr
| app f _ _, i+1 => getRevArg! f i
| _, _ => panic! "invalid index"
/-- Given `f a₀ a₁ ... aₙ`, returns the `i`th argument or panics if out of bounds. -/
@[inline] def getArg! (e : Expr) (i : Nat) (n := e.getAppNumArgs) : Expr :=
getRevArg! e (n - i - 1)
/-- Given `f a₀ a₁ ... aₙ`, returns the `i`th argument or returns `v₀` if out of bounds. -/
@[inline] def getArgD (e : Expr) (i : Nat) (v₀ : Expr) (n := e.getAppNumArgs) : Expr :=
getRevArgD e (n - i - 1) v₀
/-- Given `f a₀ a₁ ... aₙ`, returns true if `f` is a constant with name `n`. -/
def isAppOf (e : Expr) (n : Name) : Bool :=
match e.getAppFn with
| const c _ _ => c == n
| _ => false
/-- Given `f a₁ ... aᵢ`, returns true if `f` is a constant
with name `n` and has the correct number of arguments. -/
def isAppOfArity : Expr → Name → Nat → Bool
| const c _ _, n, 0 => c == n
| app f _ _, n, a+1 => isAppOfArity f n a
@ -787,6 +835,7 @@ def replaceFVars (e : Expr) (fvars : Array Expr) (vs : Array Expr) : Expr :=
instance : ToString Expr where
toString := Expr.dbgToString
/-- Returns true when the expression does not have any sub-expressions. -/
def isAtomic : Expr → Bool
| Expr.const _ _ _ => true
| Expr.sort _ _ => true
@ -854,13 +903,13 @@ def mkAppRevRange (f : Expr) (beginIdx endIdx : Nat) (revArgs : Array Expr) : Ex
Examples:
- `betaRev (fun x y => t x y) #[]` ==> `fun x y => t x y`
- `betaRev (fun x y => t x y) #[a]` ==> `fun y => t a y`
- `betaRev (fun x y => t x y) #[a, b]` ==> t b a`
- `betaRev (fun x y => t x y) #[a, b, c, d]` ==> t d c b a`
- `betaRev (fun x y => t x y) #[a, b]` ==> `t b a`
- `betaRev (fun x y => t x y) #[a, b, c, d]` ==> `t d c b a`
Suppose `t` is `(fun x y => t x y) a b c d`, then
`args := t.getAppRev` is `#[d, c, b, a]`,
and `betaRev (fun x y => t x y) #[d, c, b, a]` is `t a b c d`.
If `useZeta` is true, the function also performs zeta-reduction to create further
If `useZeta` is true, the function also performs zeta-reduction (reduction of let binders) to create further
opportunities for beta reduction.
-/
partial def betaRev (f : Expr) (revArgs : Array Expr) (useZeta := false) (preserveMData := false) : Expr :=
@ -892,6 +941,8 @@ partial def betaRev (f : Expr) (revArgs : Array Expr) (useZeta := false) (preser
mkAppRevRange (b.instantiateRange n sz revArgs) 0 n revArgs
go f 0
/-- Apply the given arguments to `f`, beta-reducing if `f` is a
lambda expression. See docstring for `betaRev` for examples. -/
def beta (f : Expr) (args : Array Expr) : Expr :=
betaRev f args.reverse
@ -901,6 +952,7 @@ def isHeadBetaTargetFn (useZeta : Bool) : Expr → Bool
| Expr.mdata _ b _ => isHeadBetaTargetFn useZeta b
| _ => false
/-- `(fun x => e) a` ==> `e[x/a]`. -/
def headBeta (e : Expr) : Expr :=
let f := e.getAppFn
if f.isHeadBetaTargetFn false then betaRev f e.getAppRevArgs else e
@ -1128,11 +1180,11 @@ private partial def getParamSubstArray (ps : Array Name) (us : Array Level) (p'
def instantiateLevelParamsArray (e : Expr) (paramNames : Array Name) (lvls : Array Level) : Expr :=
instantiateLevelParamsCore (fun p => getParamSubstArray paramNames lvls p 0) e
/- Annotate `e` with the given option. -/
/-- Annotate `e` with the given option. -/
def setOption (e : Expr) (optionName : Name) [KVMap.Value α] (val : α) : Expr :=
mkMData (MData.empty.set optionName val) e
/- Annotate `e` with `pp.explicit := true`
/-- Annotate `e` with `pp.explicit := true`
The delaborator uses `pp` options. -/
def setPPExplicit (e : Expr) (flag : Bool) :=
e.setOption `pp.explicit flag
@ -1140,7 +1192,7 @@ def setPPExplicit (e : Expr) (flag : Bool) :=
def setPPUniverses (e : Expr) (flag : Bool) :=
e.setOption `pp.universes flag
/- If `e` is an application `f a_1 ... a_n` annotate `f`, `a_1` ... `a_n` with `pp.explicit := false`,
/-- If `e` is an application `f a_1 ... a_n` annotate `f`, `a_1` ... `a_n` with `pp.explicit := false`,
and annotate `e` with `pp.explicit := true`. -/
def setAppPPExplicit (e : Expr) : Expr :=
match e with
@ -1150,7 +1202,7 @@ def setAppPPExplicit (e : Expr) : Expr :=
mkAppN f args |>.setPPExplicit true
| _ => e
/- Similar for `setAppPPExplicit`, but only annotate children with `pp.explicit := false` if
/-- Similar for `setAppPPExplicit`, but only annotate children with `pp.explicit := false` if
`e` does not contain metavariables. -/
def setAppPPExplicitForExposingMVars (e : Expr) : Expr :=
match e with