Closed Atry closed 3 years ago
We certainly do not want overlapping instances here. We need to pick just one of these definitions.
The purpose of overlapping instances is to support multiple MonadError
instances, similar to https://github.com/haskell/mtl/pull/73
Suppose we have a type:
type X = ContT IOException (ContT AllocationLimitExceeded (ContT Int IO))
It should support both the following MonadError
s:
MonadError AllocationLimitExceeded X
MonadError IOException X
I'm not a fan of overlapping instances in general, especially not with FunDeps. Overlapping allows you to subvert some of the guarantees that FunDeps give you in the first place, those guarantees which have thus far been a trademark of MTL typeclasses. See https://gitlab.haskell.org/ghc/ghc/issues/15927 for some more discussion.
Then how can we gain the ability of multiple MonadError?
chessai notifications@github.com于2020年1月18日 周六下午1:27写道:
I'm not a fan of overlapping instances in general, especially not with FunDeps. Overlapping allows you to subvert some of the guarantees that FunDeps give you in the first place, those guarantees which have thus far been a trademark of MTL typeclasses. See https://gitlab.haskell.org/ghc/ghc/issues/15927 for some more discussion.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/haskell/mtl/pull/71?email_source=notifications&email_token=AAES3OV3AXVUFV7NW43SOFTQ6NX2NA5CNFSM4KISIJP2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJKCKLI#issuecomment-575939885, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAES3OTYM2KEEGGSPKKQZADQ6NX2NANCNFSM4KISIJPQ .
I did not really understand the initial purpose of the function dependency m -> e
on MonadError
. Why not just remove m -> e
?
I did not really understand the initial purpose of the function dependency m -> e on MonadError. Why not just remove m -> e?
The dependency is there for type inference, otherwise you would have to annotate error types in a lot of places. Overlapping instances, which is your motivation for removing those dependencies, can also be quite unintuitive, which is a fairly strong reason not to use them.
For instance, this overlapping instance makes it impractical to use ContT
with a MonadError
constraint on an abstract m
:
-- Doesn't typecheck
throwe :: MonadError e m => e -> ContT r m a
throwe = throwError
It typechecks: https://repl.it/repls/CornsilkStrongDonateware
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
class MonadError e m where
throwError :: e -> m a
newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }
instance {-# OVERLAPPING #-} Monad m => MonadError e (ContT e (ContT r m)) where
throwError e = undefined
instance MonadError e m => MonadError e (ContT r m) where
throwError e = undefined
throwe :: MonadError e m => e -> ContT r m a
throwe = throwError
main = return ()
ContT e (ContT r (a lot of other transformers...))
could be more efficient thanExceptT e (ContT r (a lot of other transformers...))
, because>>=
andreturn
forContT e (ContT r m)
are as efficient asCont
, while the performance ofExceptT
depend on the number of layers of transformers.