Search code examples
haskellscotty

MonadReader instance for Scotty's ActionT


I have the type

ActionT TL.Text (ReaderT T.Text IO)

I'm trying to make a MonadReader instance for this so that I do not have to lift ask, but always get

 (All instance types must be of the form (T a1 ... an)
  where a1 ... an are *distinct type variables*,
  and each type variable appears at most once in the instance head.
  Use -XFlexibleInstances if you want to disable this)

I've tried a bunch of instance types, a couple below, but they always get the above error

instance MonadReader T.Text (ActionT TL.Text (ReaderT T.Text IO))    

instance MonadReader r (ActionT TL.Text (ReaderT r IO))

instance (ScottyError e, MonadReader r m) => MonadReader r (ActionT e (ReaderT r m))

I feel like I'm missing something fundamental with instances. I think I understand FlexibleInstances but I can't see how that applies here.

Any help with the instance type would be appreciated, I would like to implement ask and local myself as the main goal is learning.

Thanks.

Update

I got it working with FlexibleInstances, MultiParamTypeClasses, UndecidableInstances and

instance  (ScottyError e, Monad m, MonadReader r m) => MonadReader r (ActionT e m) where
  ask = lift ask

Still working on the implementation of local. I also assume UndecidableInstances is bad?

Update 2

I think what I really need is.

instance  (ScottyError e, Monad m, MonadReader r m) => MonadReader r (ActionT e (ReaderT r m)) where

But I still cant figure out local


Solution

  • The instance would be

    instance  (ScottyError e, Monad m, MonadReader r m) => MonadReader r (ActionT e (ReaderT r m)) where
    

    However Scotty does not expose the required functions to implement local.

    I am attempting to write a mapActionT to make it possible to implement local and I will update this answer if I ever figure out the types to do that :)

    Edit:

    I think I have mapActionT, but I'm not sure.

    mapActionT :: (Monad m, Functor n, ScottyError e) => (forall a . m a -> n a) -> ActionT e m b -> ActionT e n b
    mapActionT nat m = ActionT $ flip mapErrorT (runAM m) $ \rt -> 
            flip mapReaderT rt $ \st ->
                StateT $ \s -> fmap (\a -> (a,s)) (nat (evalStateT st s))
    

    I ended up copying the type signature from hoist so that I could bring in Functor n for my fmap.

    Any comments on the implementation are welcome.