int-index / ether

Monad Transformers and Classes
https://int-index.github.io/ether/
BSD 3-Clause "New" or "Revised" License
78 stars 7 forks source link

How do I upgrade to 5.*? #37

Closed martyall closed 6 years ago

martyall commented 7 years ago

I'm confused about how to add monad transformers not defined in mtl to my ether stack. For example, in 4.* I used to be able to do something like

newtype AppHandler a = AppHandler {runAppHandler :: ReaderT' AppContext (ExceptT' ServantErr (LoggingT IO)) a}
  deriving (Functor, Applicative, Monad, MonadIO, MonadReader' AppContext, MonadExcept' ServantErr,
            MonadThrow, MonadCatch)

instance MonadLogger AppHandler where
  monadLoggerLog loc src lvl msg =
    let packed = (pack $ monadLoggerLog loc src lvl msg)
    in AppHandler $ lift packed

but now pack is no longer part of the library. Can someone please tell me what is the idiomatic way to do this now?

int-index commented 7 years ago

In 4.0 pack was nothing but a synonym for the TaggedTrans constructor (albeit defined as coerce). I removed it because I decided it was unnecessary — you can use the constructor directly.

Anyway, if you liked pack, the old definition will work, just copy it to your utils module:

-- | Type-restricted 'coerce'.
pack :: trans m a -> TaggedTrans tag trans m a
pack = coerce

-- | Type-restricted 'coerce'.
unpack :: TaggedTrans tag trans m a -> trans m a
unpack = coerce
martyall commented 7 years ago

when I use this definition of pack I get overlapping instances errors coming from the GeneralizedNewtypeDeriving derivations of MonadReader' AppContext and MonadExcept' ServantErr, here's a print out for example:

        • Overlapping instances for Ether.Except.MonadExcept
                                      tag4
                                      ServantErr
                                      (TaggedTrans
                                         (TAGGED Ether.Except.EXCEPT ServantErr)
                                         (E.ExceptT ServantErr)
                                         (LoggingT IO))
            arising from a use of ‘Ether.Except.catch’
          Matching instances:
            instance [overlappable] forall k (t :: (* -> *) -> * -> *) (m :: *
                                                                             -> *) (tag :: k) e.
                                    (transformers-lift-0.2.0.1:Control.Monad.Trans.Lift.Catch.LiftCatch
                                       t,
                                     Monad (t m), Ether.Except.MonadExcept tag e m) =>
                                    Ether.Except.MonadExcept tag e (t m)
              -- Defined in ‘Ether.Except’
            ...plus one instance involving out-of-scope types
              instance forall k e (trans :: Ether.Internal.K_Trans) (m :: *
                                                                          -> *) (tag :: k).
                       (Handle Ether.Except.EXCEPT e trans, Monad m, Monad (trans m)) =>
                       Ether.Except.MonadExcept
                         tag e (TaggedTrans (TAGGED Ether.Except.EXCEPT tag) trans m)
                -- Defined in ‘Ether.Except’
          (The choice depends on the instantiation of ‘tag4, k4’
           To pick the first instance above, use IncoherentInstances
           when compiling the other instance declarations)
        • In the third argument of ‘coerce’, namely ‘Ether.Except.catch’
          In the expression:
            coerce
              @(forall (a :: TYPE ghc-prim-0.5.0.0:GHC.Types.PtrRepLifted).
                TaggedTrans (TAGGED Ether.Reader.READER AppContext) (transformers-0.5.2.0:Control.Monad.Trans.Reader.ReaderT AppContext) (ExceptT' ServantErr (LoggingT IO)) a
                -> (ServantErr
                    -> TaggedTrans (TAGGED Ether.Reader.READER AppContext) (transformers-0.5.2.0:Control.Monad.Trans.Reader.ReaderT AppContext) (ExceptT' ServantErr (LoggingT IO)) a)
                   -> TaggedTrans (TAGGED Ether.Reader.READER AppContext) (transformers-0.5.2.0:Control.Monad.Trans.Reader.ReaderT AppContext) (ExceptT' ServantErr (LoggingT IO)) a)
              @(forall (a :: TYPE ghc-prim-0.5.0.0:GHC.Types.PtrRepLifted).
                AppHandler a -> (ServantErr -> AppHandler a) -> AppHandler a)
              Ether.Except.catch
          In an equation for ‘catch’:
              catch
                = coerce
                    @(forall (a :: TYPE ghc-prim-0.5.0.0:GHC.Types.PtrRepLifted).
                      TaggedTrans (TAGGED Ether.Reader.READER AppContext) (transformers-0.5.2.0:Control.Monad.Trans.Reader.ReaderT AppContext) (ExceptT' ServantErr (LoggingT IO)) a
                      -> (ServantErr
                          -> TaggedTrans (TAGGED Ether.Reader.READER AppContext) (transformers-0.5.2.0:Control.Monad.Trans.Reader.ReaderT AppContext) (ExceptT' ServantErr (LoggingT IO)) a)
                         -> TaggedTrans (TAGGED Ether.Reader.READER AppContext) (transformers-0.5.2.0:Control.Monad.Trans.Reader.ReaderT AppContext) (ExceptT' ServantErr (LoggingT IO)) a)
                    @(forall (a :: TYPE ghc-prim-0.5.0.0:GHC.Types.PtrRepLifted).
                      AppHandler a -> (ServantErr -> AppHandler a) -> AppHandler a)
                    Ether.Except.catch
          When typechecking the code for ‘Ether.Except.catch’
            in a derived instance for ‘Ether.Except.MonadExcept
                                         ServantErr ServantErr AppHandler’:
            To see the code I am typechecking, use -ddump-deriv

Any advice?

martyall commented 7 years ago

actually, this error seems to have nothing to do with the inclusion of LoggingT, in general did something change that would interfere with the deriving strategy?

int-index commented 7 years ago

Deriving like this was never a use case I had in mind — I'm surprised it worked with 4.0. So it's quite possible that it stopped working after the latest changes.

int-index commented 6 years ago

Have you been able to find a work-around, @blinky3713? The recommended way to make Ether classes work nicely with custom defined transformers is to implement instances of Lift classes from https://hackage.haskell.org/package/transformers-lift

For instance, if you implement MonadTrans and LiftLocal, then your transformer will support lifting Ether.MonadReader. This is becasue the default {-# OVERLAPPABLE #-} instance will be used.