This refactors and improves the `#eval` command, introducing some new
features.
* Now evaluated results can be represented using `ToExpr` and pretty
printing. This means **hoverable output**. If `ToExpr` fails, it then
tries `Repr` and then `ToString`. The `eval.pp` option controls whether
or not to try `ToExpr`.
* There is now **auto-derivation** of `Repr` instances, enabled with the
`pp.derive.repr` option (default to **true**). For example:
```lean
inductive Baz
| a | b
#eval Baz.a
-- Baz.a
```
It simply does `deriving instance Repr for Baz` when there's no way to
represent `Baz`. If core Lean gets `ToExpr` derive handlers, they could
be used here as well.
* The option `eval.type` controls whether or not to include the type in
the output. For now the default is false.
* Now things like `#eval do return 2` work. It tries using
`CommandElabM`, `TermElabM`, or `IO` when the monad is unknown.
* Now there is no longer `Lean.Eval` or `Lean.MetaEval`. These each used
to be responsible for both adapting monads and printing results. The
concerns have been split into two. (1) The `MonadEval` class is
responsible for adapting monads for evaluation (it is similar to
`MonadLift`, but instances are allowed to use default data when
initializing state) and (2) finding a way to represent results is
handled separately.
* Error messages about failed instance synthesis are now more precise.
Once it detects that a `MonadEval` class applies, then the error message
will be specific about missing `ToExpr`/`Repr`/`ToString` instances.
* Fixes a bug where `Repr`/`ToString` instances can't be found by
unfolding types "under the monad". For example, this works now:
```lean
def Foo := List Nat
def Foo.mk (l : List Nat) : Foo := l
#eval show Lean.CoreM Foo from do return Foo.mk [1,2,3]
```
* Elaboration errors now abort evaluation. This eliminates some
not-so-relevant error messages.
* Now evaluating a value of type `m Unit` never prints a blank message.
* Fixes bugs where evaluating `MetaM` and `CoreM` wouldn't collect log
messages.
The `run_cmd`, `run_elab`, and `run_meta` commands are now frontends for
`#eval`.
89 lines
2.8 KiB
Text
89 lines
2.8 KiB
Text
import Lean.Meta.ExprLens
|
|
import Lean.Meta.ExprTraverse
|
|
import Lean
|
|
|
|
open Lean Meta Elab Term SubExpr
|
|
|
|
def Lean.LocalContext.subtract (Γ Δ : LocalContext) : Array Expr :=
|
|
-- have Δ = Γ ++ E
|
|
let Δ := Δ.getFVars
|
|
let Γ := Γ.getFVars
|
|
let E := Δ[:(Δ.size - Γ.size)]
|
|
E.toArray
|
|
|
|
def ExprTraversal := ∀{M : _} [Monad M] [MonadLiftT MetaM M] [MonadControlT MetaM M] [MonadOptions M], (Pos → Expr → M Expr) → Pos → Expr → M Expr
|
|
|
|
instance : Inhabited ExprTraversal where
|
|
default := traverseChildrenWithPos
|
|
|
|
partial def traverseAll : ExprTraversal := fun
|
|
| visit, p, e => visit p e >>= traverseChildrenWithPos (fun p e => traverseAll visit p e) p
|
|
|
|
def testTraversal
|
|
(traversalWithPos : ExprTraversal)
|
|
(expectedLen : Nat): TermElabM Unit := do
|
|
-- make a sample expression `e` that has all of the different kinds of expressions.
|
|
let s ← `(
|
|
∀ x y : Nat,
|
|
∀ {zz : Fin x},
|
|
∃ (z : {z: Nat // z = x + y}),
|
|
let p := z.1
|
|
p + x + y = 3
|
|
)
|
|
let e ← elabTerm s none
|
|
let Γ ← getLCtx
|
|
|
|
-- traverse `e` using the `traversalWithPos` function
|
|
-- leave `e` unmodified but at each point accumulate
|
|
-- the abstracted subexpression
|
|
let (e', subexprs) ← StateT.run (
|
|
traversalWithPos (fun p s => do
|
|
let a ← get
|
|
let Δ ← getLCtx
|
|
let E := Lean.LocalContext.subtract Γ Δ
|
|
|
|
-- check that numBinders works
|
|
let nBinders ← Lean.Core.numBinders p e
|
|
if E.size != nBinders then
|
|
throwError "bad number of binders"
|
|
|
|
set <| a.push (p, Expr.abstract s E)
|
|
return s
|
|
) Pos.root e) #[]
|
|
-- the traversal output should be equal to the original
|
|
-- that is: `traversal pure e ≡ e`
|
|
if not (← liftM $ isDefEq e e') then
|
|
throwError "\n{e} \nand \n{e'} are different!"
|
|
|
|
-- check that the number of subexpressions is what we expect
|
|
-- and if it isn't then print them out for debugging.
|
|
if subexprs.size != expectedLen then
|
|
for (p, s) in subexprs do
|
|
let ppt ← PrettyPrinter.ppExpr s
|
|
dbg_trace s!"{p}, {ppt}\n"
|
|
throwError "expected size: {expectedLen}\nactual size: {subexprs.size}"
|
|
|
|
-- for each subexpression `p`, make sure that viewSubexpr produces the same
|
|
-- subexpression as that found in the traversal.
|
|
for (p, s) in subexprs do
|
|
viewSubexpr (fun fvars t => do
|
|
let t := Expr.abstract t fvars
|
|
let de ← liftM $ isDefEq t s
|
|
if not de then
|
|
throwError "\n{t} \nand \n{s} are different!"
|
|
return ()
|
|
) p e
|
|
|
|
-- check that replaceSubexpr pure is the identity
|
|
let e' ← replaceSubexpr pure p e
|
|
if not (← liftM $ isDefEq e e') then
|
|
throwError "\n{e} \nand \n{e'} are different!"
|
|
|
|
#guard_msgs in
|
|
#eval ((do
|
|
testTraversal traverseLambdaWithPos 1
|
|
testTraversal traverseChildrenWithPos 4
|
|
testTraversal traverseAll 103
|
|
return ())
|
|
: TermElabM Unit
|
|
)
|