I have following code snippet from the haskellbook that shows step by step, how monad transformer is going to unwrap:
module OuterInner where
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Reader
-- We only need to use return once
-- because it's one big Monad
embedded :: MaybeT (ExceptT String (ReaderT () IO)) Int
embedded = return 1
maybeUnwrap :: ExceptT String (ReaderT () IO) (Maybe Int)
maybeUnwrap = runMaybeT embedded
-- Next
eitherUnwrap :: ReaderT () IO (Either String (Maybe Int))
eitherUnwrap = runExceptT maybeUnwrap
-- Lastly
readerUnwrap :: () -> IO (Either String (Maybe Int))
readerUnwrap = runReaderT eitherUnwrap
There is an exercise, that I have to wrap everything again:
embedded :: MaybeT (ExceptT String (ReaderT () IO)) Int
embedded = ??? (const (Right (Just 1)))
I tried as follows:
embedded' :: MaybeT (ExceptT String (ReaderT () IO)) Int
embedded' = MaybeT (ExceptT (ReaderT (const (Right (Just 1)))))
but the compiler complains:
D:\haskell\chapter26\src\OuterInner.hs:24:15: error:
* Couldn't match type `Either a0' with `IO'
Expected type: MaybeT (ExceptT String (ReaderT () IO)) Int
Actual type: MaybeT (ExceptT String (ReaderT () (Either a0))) Int
* In the expression:
MaybeT (ExceptT (ReaderT (const (Right (Just 1)))))
In an equation for embedded':
embedded' = MaybeT (ExceptT (ReaderT (const (Right (Just 1)))))
D:\haskell\chapter26\src\OuterInner.hs:24:32: error:
* Couldn't match type `Maybe Integer'
with `Either String (Maybe Int)'
Expected type: ReaderT () (Either a0) (Either String (Maybe Int))
Actual type: ReaderT () (Either a0) (Maybe Integer)
* In the first argument of `ExceptT', namely
`(ReaderT (const (Right (Just 1))))'
In the first argument of `MaybeT', namely
`(ExceptT (ReaderT (const (Right (Just 1)))))'
In the expression:
MaybeT (ExceptT (ReaderT (const (Right (Just 1)))))
Failed, modules loaded: none.
How to solve the exercise?
GHC typed holes extension to the rescue! It lets you plumb a _ somewhere in your expression and make GHC tell you needed type. So, let's start with
:t MaybeT (ExceptT (ReaderT (const _)))
<interactive>:1:33: error:
* Found hole: _ :: m (Either e (Maybe a))
Now we need a value of ReaderT () (Either e (Maybe a)). Plumbing _ over and over we finally arrive at value
embedded = MaybeT (ExceptT (ReaderT (const (return (Right (Just 1))))))
You have defined a MaybeT (ExceptT String (Reader ())) Int. You've missed the innermost layer, IO. Unlike the others, IO doesn't have a concrete representation, but you can of course inject pure values into it with return or pure (duh).
embedded' = MaybeT . ExceptT . ReaderT
$ (pure .) . const . Right $ Just 1
Related
I have following code snippet from haskell book:
embedded' :: MaybeT (ExceptT String (ReaderT () IO)) Int
embedded' = MaybeT (ExceptT (ReaderT (const (return (Right (Just 1))))))
The type signature of ExceptT is as follow:
newtype ExceptT e m a =
ExceptT { runExceptT :: m (Either e a)) }
Compare the type signature of the embedded' function with ExceptT type constructor:
ExceptT e m a
| | |
MaybeT (ExceptT String (ReaderT () IO) (**missing here**) ) Int
How can I just leave ExceptT as higher kinded?
I tried also:
fullType :: ExceptT String []
fullType = undefined
the compiler complains:
* Expecting one more argument to `ExceptT String []'
Expected a type, but `ExceptT String []' has kind `* -> *'
* In the type signature:
fullType :: ExceptT String []
Failed, modules loaded: none.
You probably got confused by outmost MaybeT. This type is parametrised by 2 arguments of kinds m :: * -> * and a :: *, respectively. So, that ExceptT String (ReaderT () IO) is indeed missing an a because it is an MaybeT first argument and should be of kind * -> *. Had we apply Int to ExceptT, it would turn into * and then it can't be passed to MaybeT.
Disclaimer: I am not with my Haskell environment, so this might be utter nonsense.
Put in an explicit type variable for the missing spot on ExceptT:
embedded' :: MaybeT (ExceptT String (ReaderT () IO) a) Int
embedded' = MaybeT (ExceptT (ReaderT (const (return (Right (Just 1))))))
Hopefully the compiler can figure out what a should be!
What you're running into, which shows more obviously in your fullType example, is that it's not possible to give something a type of kind * -> *. Functions, values and so forth all need to have types of kind *.
Currying a type constructor gives you a * -> *, so you have to provide something for all the parameters.
Putting a type parameter in the signature for embedded' makes it kind *, but polymorphic over a.
So this is going to be a bit long, because I'm not sure how to frame this question more generally. The good news is that I have a code sample at the bottom of the question, the idea is just to make it build & elegant :-)
I have a couple of functions which have signatures like:
calledFunction
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult)
calledFunction2
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult2)
And I'd like to get in the end a result of type ExceptT String IO MyResult3 which I get by combining MyResult & MyResult2.
Now it's very nice that calledFunction returns an Either because I can leverage:
ExceptT :: m (Either e a) -> ExceptT e m a
And I just type EitherT calledFunction and I won't have anymore m (Either MyError MyResult) but straight ExceptT MyError m MyResult). Progress!
But I also need to give to calledFunction the reader context it wants. Now, I would do that with runReaderT. I have now come to the ExceptT MyError m MyResult transformer stack, so naturally the ReaderT should go where the m is.. So ExceptT MyError (ReaderT Config IO) MyResult...
Except, how do I 'fill in' the readerT with the value to read, since it's at the bottom of the transformer stack? And if I reverse the stack to have the reader at the toplevel, then runReaderT comes naturally, but I don't see how to use EitherT to transform my Either in an ExceptT elegantly...
import Control.Monad.Reader
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Except
import Control.Monad.IO.Class
import Control.Error -- 'error' package
class HasToken a where
getToken :: a -> String
data Config = Config String
instance HasToken Config where
getToken (Config x) = x
data MyError = MyError String deriving Show
data MyResult = MyResult String
data MyResult2 = MyResult2 String
data MyResult3 = MyResult3 MyResult MyResult2
calledFunction
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult)
calledFunction = undefined
calledFunction2
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult2)
calledFunction2 = undefined
cfg = Config "test"
main = undefined
test :: ExceptT MyError IO MyResult3
test = do
-- calling runReaderT each time defeats the purpose..
r1 <- ExceptT (runReaderT calledFunction cfg)
r2 <- ExceptT (runReaderT calledFunction2 cfg)
return $ MyResult3 r1 r2
test1 = runReaderT test2 cfg
test2 :: ReaderT Config (ExceptT MyError IO) MyResult3
test2 = do
-- how to make this compile?
let cfg = Config "test"
r1 <- ExceptT calledFunction
r2 <- ExceptT calledFunction2
return $ MyResult3 r1 r2
You can use hoist from Control.Monad.Morph to run the Reader below the ExceptT:
ghci> let foo = undefined :: ExceptT () (ReaderT () IO) ()
ghci> :t hoist (flip runReaderT ()) foo
hoist (flip runReaderT ()) foo :: ExceptT () IO ()
It's also easy to do it yourself, you just have to unwrap with runExceptT, supply the environment with runReader, and re-wrap the result in the ExceptT constructor:
ghci> :t \env -> ExceptT . flip runReaderT env . runExceptT
\env -> ExceptT . flip runReaderT env . runExceptT
:: r -> ExceptT e (ReaderT r m) a -> ExceptT e m a
I'm trying to use the enter function to allow me to run my API handlers with one set of exceptions that I'll translate to Servant at a high level, but I'm having troubles with a type match.
Given this minimal set of definitions:
-- server :: Config -> Server Routes
server :: Config -> ServerT Routes (ExceptT ServantErr IO)
server c = enter runApp (handlers c)
-- runApp :: AppM :~> Handler
runApp :: ExceptT AppErr IO :~> ExceptT ServantErr IO
runApp = Nat undefined
handlers :: Config -> ServerT Routes (ExceptT AppErr IO)
handlers = undefined
I end up with this type error:
Couldn't match type `IO' with `ExceptT ServantErr IO'
arising from a functional dependency between:
constraint `servant-0.7.1:Servant.Utils.Enter.Enter
(IO ResponseReceived)
(ExceptT AppErr IO :~> ExceptT ServantErr IO)
(IO ResponseReceived)'
arising from a use of `enter'
instance `servant-0.7.1:Servant.Utils.Enter.Enter
(m a) (m :~> n) (n a)'
at <no location info>
In the expression: enter runApp (handlers c)
In an equation for `server': server c = enter runApp (handlers c)
The exception comes from the line with the call to enter. For reference, the declaration of enter:
class Enter typ arg ret | typ arg -> ret, typ ret -> arg where
enter :: arg -> typ -> ret
instance Enter (m a) (m :~> n) (n a) where
-- enter :: (m :~> n) -> m a -> n a
So, when I call enter runApp, I expect the types to go as such:
enter :: (m :~> n) -> m a -> n a
enter (runApp :: m ~ ExceptT AppErr IO :~> n ~ ExceptT ServantErr IO) :: ExceptT AppErr IO a -> ExceptT ServantErr IO a
(where above I used n ~ ExceptT ServantErr IO to illustrate my type substitutions)
And in reality, I know from other code (that I have tried to mimic, and I don't understand where I went wrong), that enter runApp should/must have this type:
enter runApp :: ServerT Routes (ExceptT AppErr IO a) -> ServerT Routes (ExceptT ServantErr IO a)
So, the questions are legion:
what are the actual types of enter runApp (not what ghci will give me, but a more descriptive explanation)?
where is that (IO ResponseReceived) constraint coming from?
how do I tweak the above code to work such that the entire handler gets passed through the natural transformation?
You cannot have a Raw endpoint in the Api type. The way to do it is to get the Raw endpoint out of the handlers and call it e.g.
server c = enter runApp (handlers c) :<|> handleRaw
data InterpreterM a = ExeInterpreter a | PropInterpreter a
newtype InterpreterMT m a = InterpreterMT { runInterpreterMT :: m (InterpreterM a) }
type Interpreter = InterpreterMT (StateT Int (ReaderT Int (ErrorT String IO)))
data Stmts = Statements Stmt Stmts | EmptyStmts
instance (Monad m) => Monad (InterpreterMT m) where
return x = InterpreterMT $ return (ExeInterpreter x)
x >>= f = InterpreterMT $ do
m <- runInterpreterMT x
case m of
(ExeInterpreter a) -> runInterpreterMT (f a)
interpreter :: Stmts -> Interpreter ()
interpreter EmptyStmts = return ()
interpreter (Statements s stmts) = lift $ local (\x -> x) (interpreter stmts)
instance MonadTrans InterpreterMT where
lift m = InterpreterMT $ do
x <- m
return $ ExeInterpreter x
Compiler gives me erorr in last line:
Couldn't match type `InterpreterMT
(StateT Int (ReaderT Int (ErrorT String IO)))'
with `StateT Int (ReaderT Int (ErrorT String IO))'
Expected type: Interpreter ()
Actual type: InterpreterMT
(InterpreterMT (StateT Int (ReaderT Int (ErrorT String IO)))) ()
In the expression: lift $ local (\ x -> x) (interpreter stmts)
In an equation for `interpreter':
interpreter (Statements s stmts)
= lift $ local (\ x -> x) (interpreter stmts)
How to repair it and why it is an error?
Okay, two problems.
First, lift has the following signature:
lift :: (MonadTrans t, Monad m) => m a -> t m a
So you have to supply non-transformed monadic action to lift. In other words, you can't use InterpreterMT in an argument to lift.
So you would need to unwrap it:
lift $ local id $ runInterpreterMT $ interpreter stmts
But now your return types don't make sense: runInterpreterMT $ ... has type
StateT Int (ReaderT Int (ErrorT String IO)) (InterpreterM ())
or, shortening transformer stack to M,
M (InterpreterM ())
while interpreter returns Interpreter (), which is
InterpreterMT (StateT Int (ReaderT Int (ErrorT String IO))) ()
or, shortening transformer stack,
InterpreterMT M ()
going back to type of lift, m a -> t m a becomes M (InterpreterM ()) -> InterpreterMT M (InterpreterM ()). (InterpreterM ()) is not ().
The root of the problem? Your InterpreterMT is not actually a monad transformer, since it doesn't really transform the monad. It transforms the value.
Easy fix? Provided that you can figure out a reasonable Monad instance for InterpreterMT (one you provided is doubtful), you can define a MonadReader instance for InterpreterMT:
instance MonadReader r m => MonadReader r (InterpreterMT m) where
local f = InterpreterMT . local f . runInterpreterMT
...
(you'll need UndecideableInstances for this particular definition)
General pattern seems to be
inInterpreter f = InterpreterMT . f . runInterpreterMT
Note that this is not a Functor.
Not sure what you want to achieve here, but this whole design seems... underdeveloped. Maybe it's a good idea to go back to drawing board and figure out what exactly you want your architecture to be like.
I have defined a simple monad transformer, EntityBuilderT, that is just a newtype over ReaderT.
data EntityBuilderState = ...
newtype EntityBuilderT m a = EntityBuilderT (ReaderT EntityBuilderState m a)
To wrap a function in a new "environment", I have written the following combinator:
withNewSource :: (Monad m) => String -> EntityBuilderT m a -> EntityBuilderT m a
withNewSource itemId builder = ...
In certain cases, I also want to build a larger transformer stack. For example:
f :: MaybeT (EntityBuilderT m) a
Obviously, I cannot apply withNewSource to this function f as the monad types no longer match. I have therefore tried to use monad-control to write a new version of such combinator.
The code I've written thus far is shown below. Though the instance definitions seems to be OK, the compiler (GHC 7.4.1) rejects the code with the following message:
Couldn't match type `IO' with `EntityBuilderT m0'
When using functional dependencies to combine
MonadBaseControl IO IO,
arising from the dependency `m -> b'
in the instance declaration in `Control.Monad.Trans.Control'
MonadBaseControl (EntityBuilderT m0) IO,
arising from a use of `control'
In the expression: control
In the expression: control $ \ run -> withNewSource itemId (run m)
I'm somewhat lost. Anyone understands what the problem really is?
{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving,
MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
import Control.Applicative (Applicative)
import Control.Monad (liftM)
import Control.Monad.Base
import Control.Monad.Trans (MonadTrans)
import Control.Monad.Trans.Control
import Control.Monad.Trans.Maybe (MaybeT)
import Control.Monad.Trans.Reader (ReaderT, withReaderT)
data EntityBuilderState
newtype EntityBuilderT m a = EntityBuilderT { unEB :: ReaderT EntityBuilderState m a }
deriving (Applicative, Functor, Monad, MonadTrans)
instance MonadBase b m => MonadBase b (EntityBuilderT m) where
liftBase = liftBaseDefault
instance MonadTransControl EntityBuilderT where
newtype StT EntityBuilderT a = StEB { unStEB :: StT (ReaderT EntityBuilderState) a }
liftWith f = EntityBuilderT $ liftWith $ \run ->
f $ liftM StEB . run . unEB
restoreT = EntityBuilderT . restoreT . liftM unStEB
instance MonadBaseControl b m => MonadBaseControl b (EntityBuilderT m) where
newtype StM (EntityBuilderT m) a = StMT { unStMT :: ComposeSt EntityBuilderT m a }
liftBaseWith = defaultLiftBaseWith StMT
restoreM = defaultRestoreM unStMT
withNewSource :: (Monad m) => String -> EntityBuilderT m a -> EntityBuilderT m a
withNewSource itemId (EntityBuilderT m) = EntityBuilderT (withReaderT undefined m)
withNewSource' :: String -> MaybeT (EntityBuilderT IO) a -> MaybeT (EntityBuilderT IO) a
withNewSource' itemId m = control $ \run -> withNewSource itemId (run m)
The problem is that, because the base monad is IO, run has type MaybeT (EntityBuilderT IO) a -> IO (StM (MaybeT (EntityBuilderT IO) a)), but you're using its return value as an EntityBuilderT IO action. Additionally, the return value of the function you pass to control must be in IO, not EntityBuilderT IO.
This is because your MonadBaseControl instance says that you lift things into the base monad of the transformed monad m; since the base of MaybeT (EntityBuilderT IO) is IO, control takes a function from RunInBase (MaybeT (EntityBuilderT IO)) IO to IO (StM (MaybeT (EntityBuilderT IO)) a).
Unfortunately, I'm not experienced enough with monad-control to suggest a solution; perhaps you could use MaybeT's MonadTransControl instance to achieve the "one level down" functionality?