import Lean
open Lean System
set_option pp.mvars false
From https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/unknown.20identifier.20in.20.60let.20var.20.3A.3D.20if.20let.60
def getOpt: String → Option String
getOpt (str: String
str : String: Type
String) : Option: Type → Type
Option String: Type
String := str: String
str
def getIO: String → IO String
getIO (str: String
str : String: Type
String) : IO: Type → Type
IO String: Type
String := pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α
pure str: String
str
def getOptIO: String → IO (Option String)
getOptIO (str: String
str : String: Type
String) : IO: Type → Type
IO (Option: Type → Type
Option String: Type
String) := pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α
pure (some: {α : Type} → α → Option α
some str: String
str)
if let return worksdefworks₁ :works₁: IO StringIOIO: Type → TypeString := do if letString: Typesomesome: {α : Type _} → α → Option αopt :=opt: StringgetOptgetOpt: String → Option String"str" then let"str": Stringret :=ret: String(<-(<- getIO opt): StringgetIOgetIO: String → IO String(<- getIO opt): Stringoptopt: String) return(<- getIO opt): Stringret else returnret: String"none""none": Stringworks₁ -- works "str"works₁: IO String
let var <- if let also worksdefworks₂ :works₂: IO StringIOIO: Type → TypeString := do letString: Typevar <- if letvar: Stringsomesome: {α : Type _} → α → Option αopt :=opt: StringgetOptgetOpt: String → Option String"str" then let"str": Stringret :=ret: String(<-(<- getIO opt): StringgetIOgetIO: String → IO String(<- getIO opt): Stringoptopt: String)(<- getIO opt): Stringpurepure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f αret elseret: Stringpurepure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α"none" return"none": Stringvarvar: Stringworks₂ -- works: "str"works₂: IO String
let var := if let gives unknown identifiererror: Unknown identifier opt
in defoops₀ :oops₀: IO StringIOIO: Type → TypeString := do letString: Typevar := if letvar: Stringsomesome: {α : Type _} → α → Option αopt :=opt: StringgetOptgetOpt: String → Option String"str" then let"str": Stringret :=ret: String(<-(<- getIO : StringgetIOgetIO: String → IO String(<- getIO : String) -- unknown identifier 'opt'): Stringret elseret: String"none" return"none": Stringvarvar: String
<-, misuse :=<-, misuse :=, gives unknown identifiererror: Unknown identifier ret
in defoops₁₁ :oops₁₁: IO StringIOIO: Type → TypeString := do letString: Typesomesome: ?_ → IO (Option String)ret :=ret: ?_getOptIOgetOptIO: String → IO (Option String)"str""str": String-- unknown identifier 'ret'
<-, misuse :=, gives type mismatcherror: Type mismatch some ret has type Option ?_ but is expected to have type IO (Option String)
in defoops₁₂ :oops₁₂: IO StringIOIO: Type → TypeString := do letString: Type:=getOptIOgetOptIO: String → IO (Option String)"str" | pure "none" -- type mismatch: `IO (Option String)` got `Option ?m.2842` pure ret"str": String
<- worksdef works₁₃: IO String
works₁₃ : IO: Type → Type
IO String: Type
String := do
let some: {α : Type _} → α → Option α
some ret: String
ret <- getOptIO: String → IO (Option String)
getOptIO "str": String
"str" | pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α
pure "none": String
"none"
pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α
pure ret: String
ret -- works
:=, misuse <-, gives type mismatcherror: Type mismatch io has type String but is expected to have type IO String
in defoops₂ :oops₂: IO StringIOIO: Type → TypeString := do letString: Typeio <-io: StringgetIOgetIO: String → IO String"str""str": String-- type mismatch: expected `IO String`, got `String`
if let, expect :=, misuse <--- def oops₃ : IO String := do
-- if let some opt <- getOpt "str" then -- unexpected token '<-'; expected ':=' or '←'
-- let ret := (<- getIO opt)
-- return ret
-- else
-- return "none"
if let, expect := (<- ... ):= gives me type mismatcherror: Type mismatch some opt has type Option ?_ but is expected to have type IO (Option String)
in defoops₄₁ :oops₄₁: IO StringIOIO: Type → TypeString := do if letString: Type:=getOptIOgetOptIO: String → IO (Option String)"str" then -- type mismatch: expected `IO (Option String)`, got `Option ?m.3586` let ret <- getIO opt pure ret else pure "none""str": String
<- gives me unexpected token '<-'; expected ':=' or '←'-- def oops₄₂ : IO String := do
-- if let some opt <- getOptIO "str" then -- unexpected token '<-'; expected ':=' or '←'
-- let ret <- getIO opt
-- pure ret
-- else
-- pure "none"
:= (<- ... ) worksdef works₄₃: IO String
works₄₃ : IO: Type → Type
IO String: Type
String := do
if let some: {α : Type _} → α → Option α
some opt: String
opt := (<- getOptIO "str"): Option String
(<- getOptIO: String → IO (Option String)
getOptIO(<- getOptIO "str"): Option String
"str": String
"str"(<- getOptIO "str"): Option String
) then
let ret: String
ret <- getIO: String → IO String
getIO opt: String
opt
pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α
pure ret: String
ret
else
pure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f α
pure "none": String
"none"
v4.32: both cases below used to fail to elaborate (an application type mismatch on the
if branches, and unknown identifier 'x' on the mut binding). The underlying do-block
elaborator bugs were fixed, so the examples now compile; the guards document the current
(fixed) behavior instead of the historical errors.
warning: Variable name x is not explicitly referenced.
The binding can be removed (if unused) or named _ (if used implicitly).
Note: This linter can be disabled with set_option linter.unusedVariables false
-- v4.32: the `if true then pure true else pure false` branches now unify, so this example -- only triggers the unused-variable linter (previously it was an application type mismatch).inexample :example: IO UnitIOIO: Type → TypeUnit := do letUnit: Type← iftrue thentrue: Boolpurepure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f αtrue elsetrue: Boolpurepure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f αfalse -- v4.32: the `Id` do-block now elaborates cleanly, so there are no messages to guard. /- -/ #guard_msgs infalse: Boolexample :example: Id UnitIdId: Type → TypeUnit := do let mutUnit: Typex ← ifx: Booltrue thentrue: Boolpurepure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f αtrue elsetrue: Boolpurepure: {f : Type → Type} → [self : Pure f] → {α : Type} → α → f αfalse if letfalse: Bool.true :=.true: Boolx thenx: Boolx :=x: Boolfalsefalse: Bool