chore: remove tryPureCoe?

Based on the discussion at
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/for.2C.20unexpected.20need.20for.20type.20ascription/near/269083574
The consensus seemed to be that "auto pure" is more confusing than its worth.
This commit is contained in:
Leonardo de Moura 2022-02-03 16:24:03 -08:00
parent 6d07092d1e
commit e9d85f49e6
30 changed files with 62 additions and 165 deletions

View file

@ -742,43 +742,6 @@ def isMonadApp (type : Expr) : TermElabM Bool := do
let some (m, _) ← isTypeApp? type | pure false
return (← isMonad? m) |>.isSome
/--
Try to coerce `a : α` into `m β` by first coercing `a : α` into ‵β`, and then using `pure`.
The method is only applied if `α` is not monadic (e.g., `Nat → IO Unit`), and the head symbol
of the resulting type is not a metavariable (e.g., `?m Unit` or `Bool → ?m Nat`).
The main limitation of the approach above is polymorphic code. As usual, coercions and polymorphism
do not interact well. In the example above, the lift is successfully applied to `true`, `false` and `!y`
since none of them is polymorphic
```
def f (x : Bool) : IO Bool := do
let y ← if x == 0 then IO.println "hello"; true else false;
!y
```
On the other hand, the following fails since `+` is polymorphic
```
def f (x : Bool) : IO Nat := do
IO.prinln x
x + x -- Error: failed to synthesize `Add (IO Nat)`
```
-/
private def tryPureCoe? (errorMsgHeader? : Option String) (m β α a : Expr) : TermElabM (Option Expr) :=
commitWhenSome? do
let doIt : TermElabM (Option Expr) := do
try
let aNew ← tryCoe errorMsgHeader? β α a none
let aNew ← mkPure m aNew
pure (some aNew)
catch _ =>
pure none
forallTelescope α fun _ α => do
if (← isMonadApp α) then
pure none
else if !α.getAppFn.isMVar then
doIt
else
pure none
/-
Try coercions and monad lifts to make sure `e` has type `expectedType`.
@ -787,13 +750,6 @@ Otherwise, we just use the basic `tryCoe`.
Extensions for monads.
Given an expected type of the form `n β`, if `eType` is of the form `α`, but not `m α`
1 - Try to coerce α` into ‵β`, and use `pure` to lift it to `n α`.
It only works if `n` implements `Pure`
If `eType` is of the form `m α`. We use the following approaches.
1- Try to unify `n` and `m`. If it succeeds, then we use
```
coeM {m : Type u → Type v} {α β : Type u} [∀ a, CoeT α a β] [Monad m] (x : m α) : m β
@ -853,14 +809,7 @@ private def tryLiftAndCoe (errorMsgHeader? : Option String) (expectedType : Expr
let tryCoeSimple : TermElabM Expr :=
tryCoe errorMsgHeader? expectedType eType e f?
let some (n, β) ← isTypeApp? expectedType | tryCoeSimple
let tryPureCoeAndSimple : TermElabM Expr := do
if autoLift.get (← getOptions) then
match (← tryPureCoe? errorMsgHeader? n β eType e) with
| some eNew => pure eNew
| none => tryCoeSimple
else
tryCoeSimple
let some (m, α) ← isTypeApp? eType | tryPureCoeAndSimple
let some (m, α) ← isTypeApp? eType | tryCoeSimple
if (← isDefEq m n) then
let some monadInst ← isMonad? n | tryCoeSimple
try expandCoe (← mkAppOptM ``Lean.Internal.coeM #[m, α, β, none, monadInst, e]) catch _ => throwMismatch
@ -887,13 +836,8 @@ private def tryLiftAndCoe (errorMsgHeader? : Option String) (expectedType : Expr
unless (← isDefEq expectedType eNewType) do throwMismatch
return eNew -- approach 3 worked
catch _ =>
/-
If `m` is not a monad, then we try to use `tryPureCoe?` and then `tryCoe?`.
Otherwise, we just try `tryCoe?`.
-/
match (← isMonad? m) with
| none => tryPureCoeAndSimple
| some _ => tryCoeSimple
/- If `m` is not a monad, then we try to use `tryCoe?`. -/
tryCoeSimple
else
tryCoeSimple

View file

@ -72,7 +72,7 @@ namespace Std.HashMap
return false
def mapValsM [Monad m] (f : β → m γ) (xs : HashMap α β) : m (HashMap α γ) :=
mkHashMap (capacity := xs.size) |> xs.foldM fun acc k v => do acc.insert k (←f v)
mkHashMap (capacity := xs.size) |> xs.foldM fun acc k v => return acc.insert k (←f v)
def mapVals (f : β → γ) (xs : HashMap α β) : HashMap α γ :=
mkHashMap (capacity := xs.size) |> xs.fold fun acc k v => acc.insert k (f v)
@ -341,17 +341,17 @@ def error (msg : String) : IO α :=
throw <| IO.userError s!"Error: {msg}."
def Array.ithVal (xs : Array String) (i : Nat) (name : String) : IO Int := do
let some unparsed xs.get? i
let some unparsed := xs.get? i
| error s!"Missing {name}"
let some parsed String.toInt? unparsed
let some parsed := String.toInt? unparsed
| error s!"Invalid {name}: `{unparsed}`"
return parsed
def main (args : List String) : IO UInt32 := do
let some path args.head?
let some path := args.head?
| error "Usage: liasolver <input file>"
let lines ← IO.FS.lines path <&> Array.filter (¬·.isEmpty)
let some headerLine lines.get? 0
let some headerLine := lines.get? 0
| error "No header line"
let header := headerLine.splitOn.toArray
let nEquations ← header.ithVal 0 "amount of equations"

View file

@ -6,7 +6,7 @@ structure M (α : Type) where
instance : Monad M where
pure x := { σ := pure x }
bind x f := { σ := do (f (← x.σ)).σ }
map f x := { σ := do f (← x.σ) }
map f x := { σ := return f (← x.σ) }
instance : MonadLiftT IO M where
monadLift {α : Type} (act : IO α) : M α :=

View file

@ -5,7 +5,7 @@ open Lean Meta
let e ← withLetDecl `y (mkConst ``Nat) (mkConst ``Nat.zero) fun y => do
let m ← mkFreshExprMVar (mkConst ``Nat)
assignExprMVar m.mvarId! y
let e mkApp2 (mkConst ``Nat.add) m y
let e := mkApp2 (mkConst ``Nat.add) m y
-- goal: construct λ y, e
dbg_trace (← ppExpr (← mkLambdaFVars #[y] e)) -- doesn't work: creates let
dbg_trace (← ppExpr (← instantiateMVars <| -- doesn't work: contains free variable
@ -18,7 +18,7 @@ open Lean Meta
withLetDecl `y (mkConst ``Nat) (mkConst ``Nat.zero) fun y => do
let m ← mkFreshExprMVar (mkConst ``Nat)
assignExprMVar m.mvarId! y
let e mkApp2 (mkConst ``Nat.add) m y
let e := mkApp2 (mkConst ``Nat.add) m y
-- goal: construct λ y, e
dbg_trace (← instantiateMVars <| -- doesn't work: contains free variable
mkLambda `y BinderInfo.default (mkConst ``Nat) (← abstract e #[y]))
@ -26,7 +26,7 @@ open Lean Meta
#eval show MetaM Unit from do
let (e, m) ← withLetDecl `y (mkConst ``Nat) (mkConst ``Nat.zero) fun y => do
let m ← mkFreshExprMVar (mkConst ``Nat) (kind := MetavarKind.syntheticOpaque)
let e mkApp2 (mkConst ``Nat.add) m y
let e := mkApp2 (mkConst ``Nat.add) m y
dbg_trace (← ppExpr e)
dbg_trace (← ppExpr (← abstract e #[y]))
let e ← instantiateMVars <| -- doesn't work: contains free variable

View file

@ -2,7 +2,7 @@ import Lean
open Lean
def encodeDecode [ToJson α] [FromJson α] (x : α) : Except String α := do
let json toJson x
let json := toJson x
fromJson? json
#eval encodeDecode Name.anonymous

View file

@ -69,26 +69,26 @@ def f' (stx : Syntax) : Unhygienic Syntax := match stx with
open Parser.Term
#eval run do
match ← `(structInstField|a := b) with
| `(Parser.Term.structInstField| $lhs:ident := $rhs) => #[lhs, rhs]
| `(Parser.Term.structInstField| $lhs:ident := $rhs) => pure #[lhs, rhs]
| _ => unreachable!
#eval run do
match ← `({ a := a : a }) with
| `({ $f:ident := $e : 0 }) => "0"
| `({ $f:ident := $e $[: $a?]?}) => "1"
| stx => "2"
| `({ $f:ident := $e : 0 }) => pure "0"
| `({ $f:ident := $e $[: $a?]?}) => pure "1"
| stx => pure "2"
#eval run `(sufficesDecl|x from x)
#eval run do
match ← `([1, 2, 3, 4]) with
| `([$x, $ys,*, $z]) => #[x, mkNullNode ys, z]
| `([$x, $ys,*, $z]) => pure #[x, mkNullNode ys, z]
| _ => unreachable!
#eval run do
match ← `([1, 2]) with
| `([$x, $y, $zs,*]) => zs.getElems
| `([$x, $ys,*]) => ys.getElems
| `([$x, $y, $zs,*]) => pure zs.getElems
| `([$x, $ys,*]) => pure ys.getElems
| _ => unreachable!
#check (match · with | `([1, $ys,*, 2, $zs,*, 3]) => _)
@ -110,6 +110,6 @@ syntax "foo" term : term
#eval run do
match mkIdent `b with
| `(a) => "0"
| `(b) => "1"
| _ => "2"
| `(a) => pure "0"
| `(b) => pure "1"
| _ => pure "2"

View file

@ -3,7 +3,7 @@ def f (x : Nat) : IO Unit := do
IO.println x
def f' (x : Nat) : IO Unit := do
discard x
discard (pure x)
IO.println x
def g (xs : Array Nat) : IO Unit := do
@ -11,7 +11,7 @@ def g (xs : Array Nat) : IO Unit := do
IO.println xs
def g' (xs : Array Nat) : IO Unit := do
discard <| xs.set! 0 1 -- Error
discard <| pure (xs.set! 0 1)
IO.println xs
def h (xs : Array Nat) : IO Unit := do

View file

@ -42,7 +42,7 @@ example : True := by
/-- My notation -/
macro "mynota" e:term : term => e
macro "mynota" e:term : term => pure e
#check mynota 1
--^ textDocument/hover

View file

@ -9,7 +9,7 @@ example : 0 < 2 :=
example : OptionM Unit := do
let y : Int ← none
let x Nat.zero
let x := Nat.zero
--^ $/lean/plainTermGoal
return ()

View file

@ -26,8 +26,8 @@
{"textDocument": {"uri": "file://plainTermGoal.lean"},
"position": {"line": 11, "character": 10}}
{"range":
{"start": {"line": 11, "character": 10}, "end": {"line": 11, "character": 18}},
"goal": "y : Int\n⊢ OptionM Nat"}
{"start": {"line": 9, "character": 26}, "end": {"line": 13, "character": 11}},
"goal": "⊢ OptionM Unit"}
{"textDocument": {"uri": "file://plainTermGoal.lean"},
"position": {"line": 16, "character": 17}}
{"range":

View file

@ -1,7 +1,7 @@
syntax "foo" &"bla" term : term
macro_rules
| `(foo bla $x) => x
| `(foo bla $x) => pure x
-- bla is still a valid identifier
def bla := 10

View file

@ -53,9 +53,9 @@ inductive Kind
(msg : String :=
let complementaryName? : Option String := OptionM.run do
if inputFlag.isShort then
s!" (`--{flag.longName}`)"
pure s!" (`--{flag.longName}`)"
else
s!" (`-{← flag.shortName?}`)"
pure s!" (`-{← flag.shortName?}`)"
s!"Duplicate flag `{inputFlag}`.")
| redundantFlagArg
(flag : Flag)

View file

@ -6,7 +6,7 @@ def build (n : Nat) : Array Unit := Id.run <| do
@[noinline] def size : IO Nat := pure 50000
def bench (f : ∀ {α : Type}, α → IO Unit := fun _ => ()) : IO Unit := do
def bench (f : ∀ {α : Type}, α → IO Unit := fun _ => pure ()) : IO Unit := do
let n ← size
let arr := build n
timeit "time" $

View file

@ -2,7 +2,7 @@ import Lean
open Lean
def encodeDecode [ToJson α] [FromJson α] (x : α) : Except String α := do
let json toJson x
let json := toJson x
fromJson? json
#eval IO.ofExcept <| encodeDecode (Name.mkNum Name.anonymous 5)

View file

@ -26,7 +26,7 @@ def checkDelab (e : Expr) (tgt? : Option Syntax) (name? : Option Name := none) :
let e' ← instantiateMVars e'
-- let ⟨e', _⟩ ← levelMVarToParam e'
throwErrorIfErrors
e'
pure e'
catch ex => throwError "{pfix} failed to re-elaborate,\n{stx}\n{← ex.toMessageData.toString}"
withTheReader Core.Context (fun ctx => { ctx with options := ctx.options.setBool `pp.all true }) do
@ -54,7 +54,7 @@ syntax (name := testDelabTDN) "#testDelabN " ident : command
| `(#testDelabN $name:ident) => liftTermElabM `delabTD do
let name := name.getId
let [name] ← resolveGlobalConst (mkIdent name) | throwError "cannot resolve name"
let some cInfo (← getEnv).find? name | throwError "no decl for name"
let some cInfo := (← getEnv).find? name | throwError "no decl for name"
let some value ← pure cInfo.value? | throwError "decl has no value"
modify fun s => { s with levelNames := cInfo.levelParams }
withTheReader Core.Context (fun ctx => { ctx with currNamespace := name.getPrefix, openDecls := [] }) do
@ -270,7 +270,7 @@ set_option pp.proofs.withType false in
#testDelab ∀ (α : Type u) (vals vals_1 : List α), { data := vals : Array α } = { data := vals_1 : Array α }
expecting ∀ (α : Type u) (vals vals_1 : List α), { data := vals : Array α } = { data := vals_1 }
#testDelab (do let ctxCore ← readThe Core.Context; ctxCore.currNamespace : MetaM Name)
#testDelab (do let ctxCore ← readThe Core.Context; pure ctxCore.currNamespace : MetaM Name)
expecting do
let ctxCore ← readThe Core.Context
pure ctxCore.currNamespace

View file

@ -116,8 +116,8 @@ 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"; true) false;
!y
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)

View file

@ -6,7 +6,7 @@ theorem zero_lt_of_lt : {a b : Nat} → a < b → 0 < b
def fold {m α β} [Monad m] (as : Array α) (b : β) (f : α → β → m β) : m β := do
let rec loop : (i : Nat) → i ≤ as.size → β → m β
| 0, h, b => b
| 0, h, b => pure b
| i+1, h, b => do
have h' : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self i) h
have : as.size - 1 < as.size := Nat.sub_lt (zero_lt_of_lt h') (by decide)

View file

@ -7,7 +7,7 @@ structure Payload :=
@[noinline] def get? (p : Payload) (k : Nat) : OptionM Nat :=
if p.key == k then p.val else none
if p.key == k then pure p.val else failure
inductive T
| a (i : Nat)

View file

@ -18,7 +18,7 @@ def tst : MetaM Unit := do
trace[Meta.debug] "{m2} : {← inferType m2}"
trace[Meta.debug] "{m1} : {← inferType m1}"
let e ← mkForallFVars #[x] m2 -- `forall (x : f ?m2), ?m2`
trace[Meta.debug] "{e} : {← e}"
trace[Meta.debug] "{e} : {← inferType e}"
return ()
set_option trace.Meta.isDefEq true

View file

@ -8,11 +8,11 @@ def checkWithMkMatcherInput (matcher : Lean.Name) : Lean.MetaM Unit :=
throwError "matcher name not reconstructed correctly: {matcher} ≟ {input.matcherName}"
let lCtx ← Lean.getLCtx
let fvars Lean.collectFVars {} res.matcher
let closure Lean.Meta.Closure.mkLambda (fvars.fvarSet.toList.toArray.map lCtx.get!) res.matcher
let fvars := Lean.collectFVars {} res.matcher
let closure := Lean.Meta.Closure.mkLambda (fvars.fvarSet.toList.toArray.map lCtx.get!) res.matcher
let origTy := origMatcher.value!
let newTy closure
let newTy := closure
if not <| ←Lean.Meta.isDefEq origTy newTy then
throwError "matcher {matcher} does not round-trip correctly:\n{origTy} ≟ {newTy}"

View file

@ -103,7 +103,7 @@ def extractXY : Lean.Expr → Lean.MetaM Coords
let y ← Lean.Meta.whnf sizeArgs[1]
let numCols := (Lean.Expr.natLit? x).get!
let numRows := (Lean.Expr.natLit? y).get!
Coords.mk numCols numRows
return Coords.mk numCols numRows
partial def extractWallList : Lean.Expr → Lean.MetaM (List Coords)
| exp => do
@ -113,8 +113,8 @@ partial def extractWallList : Lean.Expr → Lean.MetaM (List Coords)
then let consArgs := Lean.Expr.getAppArgs exp'
let rest ← extractWallList consArgs[2]
let ⟨wallCol, wallRow⟩ ← extractXY consArgs[1]
(Coords.mk wallCol wallRow) :: rest
else [] -- "List.nil"
return (Coords.mk wallCol wallRow) :: rest
else return [] -- "List.nil"
partial def extractGameState : Lean.Expr → Lean.MetaM GameState
| exp => do

View file

@ -12,8 +12,8 @@ syntax "json " json : term
/- declare a micro json parser, so we can write our tests in a more legible way. -/
open Json in macro_rules
| `(json $s:strLit) => s
| `(json $n:numLit) => n
| `(json $s:strLit) => pure s
| `(json $n:numLit) => pure n
| `(json { $[$ns : $js],* }) => do
let mut fields := #[]
for n in ns, j in js do
@ -28,15 +28,15 @@ open Json in macro_rules
def checkToJson [ToJson α] (obj : α) (rhs : Json) : MetaM Unit :=
let lhs := (obj |> toJson).pretty
if lhs == rhs.pretty then
()
pure ()
else
throwError "{lhs} ≟ {rhs}"
def checkRoundTrip [Repr α] [BEq α] [ToJson α] [FromJson α] (obj : α) : MetaM Unit :=
def checkRoundTrip [Repr α] [BEq α] [ToJson α] [FromJson α] (obj : α) : MetaM Unit := do
let roundTripped := obj |> toJson |> fromJson?
if let some roundTripped := roundTripped then
if let Except.ok roundTripped := roundTripped then
if obj == roundTripped then
()
pure ()
else
throwError "{repr obj} ≟ {repr roundTripped}"
else

View file

@ -39,7 +39,7 @@ do traceCtx `module $ do {
-- if `trace.slow is active.
trace[slow] (m!"slow message: " ++ toString (slow b))
-- This is true even if it is a monad computation:
trace[slow] (m!"slow message: " ++ (toString (slow b)))
trace[slow] (m!"slow message: " ++ (toString (slow b)))
def run (x : M Unit) : M Unit :=
withReader

View file

@ -1,47 +0,0 @@
def m1 : IO Bool :=
pure true
def p (x : Nat) : Bool :=
x > 0
def tst1 : IO Bool :=
true <&&> m1
def tst2 (x : Nat) : IO Bool :=
x = 0 <&&> m1
def tst3 (x : Nat) : IO Unit := do
if ← (m1 <&&> x > 0) then
throw $ IO.userError "test"
def tst4 (x : Nat) : IO Unit := do
if x > 0 && (← m1) then
throw $ IO.userError "test"
def tst5 (x : Nat) : IO Unit := do
if ← (p x <&&> m1) then
throw $ IO.userError "test"
def tst6 (x : Nat) : IO Unit := do
if ← (p x <&&> id m1) then
throw $ IO.userError "test"
def tst7 (x : Nat) : IO Unit := do
if (← m1) && x > 0 then
throw $ IO.userError "test"
def tst8 (x : Nat) : IO Unit := do
if x > 0 && (← m1) then
throw $ IO.userError "test"
def tst9 (x : Nat) : IO Unit := do
if p x && (← m1) then
throw $ IO.userError "test"
def tst10 (x : Nat) : IO Unit := do
if p x && (← id m1) then
throw $ IO.userError "test"
def tst11 (x : Nat) : IO Unit := do
if p x && id (← m1) then
throw $ IO.userError "test"

View file

@ -6,7 +6,7 @@ def f (xs : List Nat) (delta : Nat) : List Nat :=
runST (fun ω => visit xs |>.run)
where
visit {ω} : List Nat → MonadCacheT Nat Nat (ST ω) (List Nat)
| [] => []
| [] => return []
| a::as => do
let b ← checkCache a fun _ => return a + delta
return b :: (← visit as)

View file

@ -18,7 +18,7 @@ def main : IO Unit := do
let diag := diags.getLast!
FS.writeFile "content_diag.json.produced" (toString <| toJson (diag : JsonRpc.Message))
if let some (refDiag : JsonRpc.Notification PublishDiagnosticsParams) :=
if let Except.ok (refDiag : JsonRpc.Notification PublishDiagnosticsParams) :=
(Json.parse $ ←FS.readFile "content_diag.json") >>= fromJson?
then
assert! (diag == refDiag)

View file

@ -23,7 +23,7 @@ def main : IO Unit := do
let diag := diags.getLast!
FS.writeFile "edits_diag.json.produced" (toString <| toJson (diag : JsonRpc.Message))
if let some (refDiag : JsonRpc.Notification PublishDiagnosticsParams) :=
if let Except.ok (refDiag : JsonRpc.Notification PublishDiagnosticsParams) :=
(Json.parse $ ←FS.readFile "edits_diag.json") >>= fromJson?
then
assert! (diag == refDiag)

View file

@ -3,7 +3,7 @@ namespace Foo
syntax "foo" term : term
macro_rules
| `(foo $x) => x
| `(foo $x) => pure x
set_option trace.Elab true in
#check foo true
@ -15,7 +15,7 @@ namespace Bla
syntax (name := bla) "bla" term : term
macro_rules
| `(bla $x) => x
| `(bla $x) => pure x
set_option trace.Elab true in
#check bla true

View file

@ -7,5 +7,5 @@ open Lean
initialize registerBuiltinAttribute {
name := `bar,
descr := "",
add := fun _ _ _ => ()
add := fun _ _ _ => pure ()
}

View file

@ -13,4 +13,4 @@ def tst : MetaM Unit := do
unsafe def main : IO Unit := do
initSearchPath (← Lean.findSysroot?) ["build"]
withImportModules [{ module := `UserAttr.Tst : Import }] {} 0 fun env => ()
withImportModules [{ module := `UserAttr.Tst : Import }] {} 0 fun env => pure ()