Open JamesGallicchio opened 10 months ago
One potential type-class-based solution that eliminates the need for throwThe
at the cost of losing support for implicit-type .
constructors (e.g., throw (.userError "hi")
would have to be written as throw (IO.userError "hi")
):
namespace Issue3128
#check show IO Unit from throw "hi" -- fails
#check show IO Unit from throw (IO.userError "hi") -- works
#check show IO Unit from throw (.userError "hi") -- works
class MonadThrow (ε : semiOutParam $ Type u) (m : Type v → Type w) where
throw : ε → m α
export MonadThrow (throw)
abbrev throwThe (ε : Type u) {m : Type v → Type w} [MonadThrow ε m] {α : Type v} (e : ε) : m α :=
throw e
instance [MonadExceptOf ε m] : MonadThrow ε m where
throw := MonadExceptOf.throw
instance [MonadThrow α m] [Coe β α] : MonadThrow β m where
throw e := throwThe α ↑e
#check show IO Unit from throw "hi" -- works
#check show IO Unit from throw (IO.userError "hi") -- works
#check show IO Unit from throw (.userError "hi") -- fails
@tydeu's suggestion looks like a net positive to me.
It would be good to first look at whether support for the combination of out-params and coercions cannot be improved in general. There already is special handling for out-params in the app result type, but not yet in arguments.
Revisiting this, I have since discovered that @[default_instance]
can be used to get the best of both worlds:
namespace Issue3128
#check show IO Unit from throw "hi" -- fails
#check show IO Unit from throw (IO.userError "hi") -- works
#check show IO Unit from throw (.userError "hi") -- works
class MonadThrow (ε : semiOutParam $ Type u) (m : Type v → Type w) where
throw : ε → m α
export MonadThrow (throw)
abbrev throwThe (ε : Type u) {m : Type v → Type w} [MonadThrow ε m] {α : Type v} (e : ε) : m α :=
throw e
@[default_instance] instance [MonadExcept ε m] : MonadThrow ε m where
throw := MonadExcept.throw
instance [MonadThrow α m] [Coe β α] : MonadThrow β m where
throw e := throwThe α ↑e
#check show IO Unit from throw "hi" -- works
#check show IO Unit from throw (IO.userError "hi") -- works
#check show IO Unit from throw (.userError "hi") -- works
Also, if the use of coercions in synthesis is a concern, we can avoid them altogether with instances like:
instance [MonadThrow IO.Error m] : MonadThrow String m where
throw e := throwThe IO.Error e
Proposal
Currently,
throw "error" : IO A
does not typecheck. I would like it to typecheck.Reasoning:
.userError
when trying to throw inIO
. I suspect this is deeply unfriendly to beginners.String
toIO.Error
, via.userError
.IO
almost always wants to throw via this canonical coercion, rather than the more internal-to-IO
errors.I have no ideas for how to do this without some major changes to
MonadExcept
. Suggestions appreciated.One (bad) option would be to add a
MonadExceptOf
instance:but this requires
throwThe String
rather thanthrow
, which I find no better than the status quo.Community Feedback
Small bit of discussion here: https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Map.20of.20Lean's.20monads
Impact
Add :+1: to issues you consider important. If others benefit from the changes in this proposal being added, please ask them to add :+1: to it.