问题
I'm refactoring some old code, which is in a polymorphic, but type-class constrained, monad:
class ( MonadIO m
, MonadLogger m
, MonadLoggerIO m
, MonadThrow m
, MonadCatch m
, MonadMask m
, MonadBaseControl IO m
, MonadUnliftIO) => HasLogging m where
In the older code the application's main monad was...
type AppM = ReaderT Env IO
...which will now change to...
newtype AppM (features :: [FeatureFlag]) a = AppM (ReaderT Env IO a)
deriving (Functor, Applicative, Monad, MonadReader Env, MonadIO)
Given this context, is it safe to derive the following, automatically:
- MonadThrow
- MonadCatch
- MonadMask
- MonadBaseControl
- MonadUliftIO
Without getting into GHC internals, what's the best way to develop intuition about what's actually happening when the compiler derives things automagically?
回答1:
The user manual has documentation about every extension, and it keeps getting better; here's the section on deriving, that should be sufficient to know what's actually happening: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#extensions-to-the-deriving-mechanism
In this case, all those classes are handled by GeneralizedNewtypeDeriving
.
{-# LANGUAGE GeneralizedNewtypeDeriving, UndecidableInstances #-}
module M where
import Control.Monad.IO.Unlift
import Control.Monad.Catch
import Control.Monad.Trans.Control
import Control.Monad.Base
import Control.Monad.Reader
newtype Foo a = Foo (ReaderT () IO a)
deriving (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, MonadThrow, MonadCatch, MonadMask, MonadBase IO, MonadBaseControl IO)
In general, the three relevant extensions for user-defined classes are GeneralizedNewtypeDeriving
, DerivingVia
, and DeriveAnyType
. And it's also worth enabling DerivingStrategies
to make it explicit which is being used.
来源:https://stackoverflow.com/questions/56863771/is-it-safe-to-derive-monadthrow-monadcatch-monadbasecontrol-monadunliftio-et