一个例子是 Except.tryCatch:
Except.tryCatch.{u, v} {ε : Type u} {α : Type v}
(ma : Except ε α) (handle : ε → Except ε α) :
Except ε α
它的两个参数都位于 Except ε 中。
MonadLift 可以提升处理器的整个应用。
函数 getBytes 使用状态和异常从 Nat 数组中提取各个字节;为了明确展示其结构,编写时没有使用 Lean.Parser.Term.do : termdo 记法或自动提升。
set_option autoLift false
def getByte (n : Nat) : Except String UInt8 :=
if n < 256 then
pure n.toUInt8
else throw s!"Out of range: {n}"
def getBytes (input : Array Nat) :
StateT (Array UInt8) (Except String) Unit := do
input.forM fun i =>
liftM (Except.tryCatch (some <$> getByte i) fun _ => pure none) >>=
fun
| some b => modify (·.push b)
| none => pure ()
Except.ok #[1, 58, 255, 2]#eval getBytes #[1, 58, 255, 300, 2, 1000000] |>.run #[] |>.map (·.2)
Except.ok #[1, 58, 255, 2]
getBytes 使用提升后的动作所返回的 Option 来表示所需的状态更新。
如果对内部动作有多种响应方式,例如保存已处理的异常,这种做法很快就会变得难以驾驭。
理想情况下,应当直接在 tryCatch 调用内部执行状态更新。
然而,尝试保存字节和已处理的异常并不可行,因为 Except.tryCatch 的实参类型为 Except String Unit:
def getBytes' (input : Array Nat) :
StateT (Array String)
(StateT (Array UInt8)
(Except String)) Unit := do
input.forM fun i =>
liftM
(Except.tryCatch
(getByte i >>= fun b =>
failed to synthesize instance of type class
MonadStateOf (Array UInt8) (Except String)
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.modifyThe (Array UInt8) (·.push b))
fun e =>
failed to synthesize instance of type class
MonadStateOf (Array String) (Except String)
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.modifyThe (Array String) (·.push e))
failed to synthesize instance of type class
MonadStateOf (Array String) (Except String)
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
因为 StateT 有一个 MonadControl 实例,所以可以用 control 代替 liftM。
它为内部动作提供外部单子的解释器。
对于 StateT,该解释器期望内部单子返回一个包含更新后状态的元组,并负责提供初始状态以及从元组中提取更新后的状态。
def getBytes' (input : Array Nat) :
StateT (Array String)
(StateT (Array UInt8)
(Except String)) Unit := do
input.forM fun i =>
control fun run =>
(Except.tryCatch
(getByte i >>= fun b =>
run (modifyThe (Array UInt8) (·.push b))))
fun e =>
run (modifyThe (Array String) (·.push e))
Except.ok (#["Out of range: 300", "Out of range: 1000000"], #[1, 58, 255, 2])#eval
getBytes' #[1, 58, 255, 300, 2, 1000000]
|>.run #[] |>.run #[]
|>.map (fun (((), bytes), errs) => (bytes, errs))
Except.ok (#["Out of range: 300", "Out of range: 1000000"], #[1, 58, 255, 2])