ScottyT does not have a MonadTrans instance
It seems that ScottyT is defined as
newtype ScottyT e m a = ScottyT { runS :: State (ScottyState e m) a }
Is there a reason this isn't using StateT?
newtype ScottyT e m a = ScottyT { runS :: StateT (ScottyState e m) m a }
This would unlock MonadTrans and other useful instances within ScottyT and fulfill the T label on this type.
The difference between m and n are the only blockers I can find for using StateT. Is there a reason these constraints are setup this way?
scottyT :: (Monad m, MonadIO n)
=> Port
-> (m Response -> IO Response) -- ^ Run monad 'm' into 'IO', called at each action.
-> ScottyT e m ()
-> n ()
i agree that this should be done. i'm not sure what should be done to fix the scottyT function. that function cannot evaluate the ScottyT argument in n, so it cannot really use execStateT just once, as it does now. i'll think on this.
There are two problems:
- ScottyT is not a monad transformer
- Some code already relies on ScottyT being an IO-based monad
To make it a real transformer, a new name needs to be introduced, but that kinda kills the original motivation. Do you have any opinions?
The difference between
mandnare the only blockers I can find for usingStateT. Is there a reason these constraints are setup this way?scottyT :: (Monad m, MonadIO n) => Port -> (m Response -> IO Response) -- ^ Run monad 'm' into 'IO', called at each action. -> ScottyT e m () -> n ()
@eborden I know it's been a few years :) But the reasonm and n are different is because ScottyT is essentially just a configuration effect for the server, whereas ActionT is where the exchange with the world takes place. If m == n we'd have to run the same effect both at every endpoint response and to run the overall server, which I think is what @chessai hinted at above.
I'm now leaning towards a breaking change that adds a second type parameter to ScottyT: ScottyT n m a, and add a MonadTrans (ScottyT n) instance that way.