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?
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
.In general, the three relevant extensions for user-defined classes are
GeneralizedNewtypeDeriving
,DerivingVia
, andDeriveAnyType
. And it's also worth enablingDerivingStrategies
to make it explicit which is being used.