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`.
266 lines
4.6 KiB
Text
266 lines
4.6 KiB
Text
def f (x : Nat) : IO Nat := do
|
||
IO.println "hello world"
|
||
let aux (y : Nat) (z : Nat) : IO Nat := do
|
||
IO.println "aux started"
|
||
IO.println s!"y: {y}, z: {z}"
|
||
pure (x+y)
|
||
discard <| aux x
|
||
(x + 1) -- It is part of the application since it is indented
|
||
discard <| aux x (x -- parentheses use `withoutPosition`
|
||
-1)
|
||
discard <| aux x x;
|
||
aux x
|
||
x
|
||
|
||
/--
|
||
info: hello world
|
||
aux started
|
||
y: 10, z: 11
|
||
aux started
|
||
y: 10, z: 9
|
||
aux started
|
||
y: 10, z: 10
|
||
aux started
|
||
y: 10, z: 10
|
||
---
|
||
info: 20
|
||
-/
|
||
#guard_msgs in
|
||
#eval f 10
|
||
|
||
def g (xs : List Nat) : StateT Nat Id Nat := do
|
||
let mut xs := xs
|
||
if xs.isEmpty then
|
||
xs := [← get]
|
||
dbg_trace ">>> xs: {xs}"
|
||
return xs.length
|
||
|
||
/--
|
||
info: >>> xs: [1, 2, 3]
|
||
---
|
||
info: 3
|
||
-/
|
||
#guard_msgs in
|
||
#eval g [1, 2, 3] |>.run' 10
|
||
|
||
/--
|
||
info: >>> xs: [10]
|
||
---
|
||
info: 1
|
||
-/
|
||
#guard_msgs in
|
||
#eval g [] |>.run' 10
|
||
|
||
theorem ex1 : (g [1, 2, 4, 5] |>.run' 0) = 4 :=
|
||
rfl
|
||
|
||
theorem ex2 : (g [] |>.run' 0) = 1 :=
|
||
rfl
|
||
|
||
def h (x : Nat) (y : Nat) : Nat := Id.run <| do
|
||
let mut x := x
|
||
let mut y := y
|
||
if x > 0 then
|
||
let y' := x + 1
|
||
x := y'
|
||
else
|
||
y := y + 1
|
||
return x + y
|
||
|
||
theorem ex3 (y : Nat) : h 0 y = 0 + (y + 1) :=
|
||
rfl
|
||
|
||
theorem ex4 (y : Nat) : h 1 y = (1 + 1) + y :=
|
||
rfl
|
||
|
||
def sumOdd (xs : List Nat) (threshold : Nat) : Nat := Id.run <| do
|
||
let mut sum := 0
|
||
for x in xs do
|
||
if x % 2 == 1 then
|
||
sum := sum + x
|
||
if sum > threshold then
|
||
break
|
||
unless x % 2 == 1 do
|
||
continue
|
||
dbg_trace ">> x: {x}"
|
||
return sum
|
||
|
||
/--
|
||
info: >> x: 1
|
||
>> x: 3
|
||
>> x: 5
|
||
---
|
||
info: 16
|
||
-/
|
||
#guard_msgs in
|
||
#eval sumOdd [1, 2, 3, 4, 5, 6, 7, 9, 11, 101] 10
|
||
|
||
theorem ex5 : sumOdd [1, 2, 3, 4, 5, 6, 7, 9, 11, 101] 10 = 16 :=
|
||
rfl
|
||
|
||
-- We need `Id.run` because we still have `Monad Option`
|
||
def find? (xs : List Nat) (p : Nat → Bool) : Option Nat := Id.run do
|
||
let mut result := none
|
||
for x in xs do
|
||
if p x then
|
||
result := x
|
||
break
|
||
return result
|
||
|
||
def sumDiff (ps : List (Nat × Nat)) : Nat := Id.run do
|
||
let mut sum := 0
|
||
for (x, y) in ps do
|
||
sum := sum + x - y
|
||
return sum
|
||
|
||
theorem ex7 : sumDiff [(2, 1), (10, 5)] = 6 :=
|
||
rfl
|
||
|
||
def f1 (x : Nat) : IO Unit := do
|
||
let rec loop : Nat → IO Unit
|
||
| 0 => pure ()
|
||
| x+1 => do IO.println x; loop x
|
||
loop x
|
||
|
||
/--
|
||
info: 9
|
||
8
|
||
7
|
||
6
|
||
5
|
||
4
|
||
3
|
||
2
|
||
1
|
||
0
|
||
-/
|
||
#guard_msgs in
|
||
#eval f1 10
|
||
|
||
partial def f2 (x : Nat) : IO Unit := do
|
||
let rec
|
||
isEven : Nat → Bool
|
||
| 0 => true
|
||
| x+1 => isOdd x,
|
||
isOdd : Nat → Bool
|
||
| 0 => false
|
||
| x+1 => isEven x
|
||
IO.println ("isOdd(" ++ toString x ++ "): " ++ toString (isOdd x))
|
||
|
||
/-- info: isOdd(11): true -/
|
||
#guard_msgs in
|
||
#eval f2 11
|
||
|
||
/-- info: isOdd(10): false -/
|
||
#guard_msgs in
|
||
#eval f2 10
|
||
|
||
def split (xs : List Nat) : List Nat × List Nat := Id.run do
|
||
let mut evens := []
|
||
let mut odds := []
|
||
for x in xs.reverse do
|
||
if x % 2 == 0 then
|
||
evens := x :: evens
|
||
else
|
||
odds := x :: odds
|
||
return (evens, odds)
|
||
|
||
theorem ex8 : split [1, 2, 3, 4] = ([2, 4], [1, 3]) :=
|
||
rfl
|
||
|
||
def f3 (x : Nat) : IO Bool := do
|
||
let y ← cond (x == 0) (do IO.println "hello"; pure true) (pure false);
|
||
pure !y
|
||
|
||
def f4 (x y : Nat) : Nat × Nat := Id.run <| do
|
||
let mut (x, y) := (x, y)
|
||
match x with
|
||
| 0 => y := y + 1
|
||
| _ => x := x + y
|
||
return (x, y)
|
||
|
||
#guard f4 0 10 == (0, 11)
|
||
#guard f4 5 10 == (15,10)
|
||
|
||
theorem ex9 (y : Nat) : f4 0 y = (0, y+1) :=
|
||
rfl
|
||
|
||
theorem ex10 (x y : Nat) : f4 (x+1) y = ((x+1)+y, y) :=
|
||
rfl
|
||
|
||
def f5 (x y : Nat) : Nat × Nat := Id.run <| do
|
||
let mut (x, y) := (x, y)
|
||
match x with
|
||
| 0 => y := y + 1
|
||
| z+1 => dbg_trace "z: {z}"; x := x + y
|
||
return (x, y)
|
||
|
||
/--
|
||
info: z: 4
|
||
---
|
||
info: (11, 6)
|
||
-/
|
||
#guard_msgs in
|
||
#eval f5 5 6
|
||
|
||
theorem ex11 (x y : Nat) : f5 (x+1) y = ((x+1)+y, y) :=
|
||
rfl
|
||
|
||
def f6 (x : Nat) : Nat := Id.run <| do
|
||
let mut x := x
|
||
if x > 10 then
|
||
return 0
|
||
x := x + 1
|
||
return x
|
||
|
||
theorem ex12 : f6 11 = 0 :=
|
||
rfl
|
||
|
||
theorem ex13 : f6 5 = 6 :=
|
||
rfl
|
||
|
||
def findOdd (xs : List Nat) : Nat := Id.run <| do
|
||
for x in xs do
|
||
if x % 2 == 1 then
|
||
return x
|
||
return 0
|
||
|
||
theorem ex14 : findOdd [2, 4, 5, 8, 7] = 5 :=
|
||
rfl
|
||
|
||
theorem ex15 : findOdd [2, 4, 8, 10] = 0 :=
|
||
rfl
|
||
|
||
def f7 (ref : IO.Ref (Option (Nat × Nat))) : IO Nat := do
|
||
let some (x, y) ← ref.get | pure 100
|
||
IO.println (toString x ++ ", " ++ toString y)
|
||
return x+y
|
||
|
||
def f7Test : IO Unit := do
|
||
unless (← f7 (← IO.mkRef (some (10, 20)))) == 30 do throw $ IO.userError "unexpected"
|
||
unless (← f7 (← IO.mkRef none)) == 100 do throw $ IO.userError "unexpected"
|
||
|
||
/-- info: 10, 20 -/
|
||
#guard_msgs in
|
||
#eval f7Test
|
||
|
||
def f8 (x : Nat) : IO Nat := do
|
||
let y ←
|
||
if x == 0 then
|
||
IO.println "x is zero"
|
||
return 100 -- returns from the `do`-block
|
||
else
|
||
pure (x + 1)
|
||
IO.println ("y: " ++ toString y)
|
||
return y
|
||
|
||
def f8Test : IO Unit := do
|
||
unless (← f8 0) == 100 do throw $ IO.userError "unexpected"
|
||
unless (← f8 1) == 2 do throw $ IO.userError "unexpected"
|
||
|
||
/--
|
||
info: x is zero
|
||
y: 2
|
||
-/
|
||
#guard_msgs in
|
||
#eval f8Test
|