While building a monad stack with monad transformers to write a library, I hit a question about the behavior of it.
The following code won't pass the type checker:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Foo (FooM, runFooM, foo) where
import Control.Applicative
import Control.Monad.Reader
newtype FooM m a = FooM { runFooM :: ReaderT Int m a }
deriving (Functor, Applicative, Monad, MonadReader Int)
foo :: FooM m Int
foo = do
x <- ask
return x
The error is:
$ ghc foo.hs
[1 of 1] Compiling Foo ( foo.hs, foo.o )
foo.hs:12:3:
No instance for (Monad m) arising from a do statement
Possible fix:
add (Monad m) to the context of
the type signature for foo :: FooM m Int
In a stmt of a 'do' block: x <- ask
In the expression:
do { x <- ask;
return x }
In an equation for ‘foo’:
foo
= do { x <- ask;
return x }
The fix is easy as GHC suggests, just adds Monad constraint to the foo function:
foo :: Monad m => FooM m Int
foo = do
x <- ask
return x
But here, the foo function only asks the FooM value to give its Int value and it is already an (automatically derived) MonadReader instance.
So I think Monad constraint is not required to m.
I guess this relates to the implementation of the monad transformers (I use mlt==2.2.1),
but I cannot figure out the exact reason.
I may miss something obvious though.
Could you explain why this doesn't pass the checker?
Thanks.
It's because the Monad instance for ReaderT is defined as
instance Monad m => Monad (ReaderT r m)
i.e. the type ReaderT r m is an instance of Monad only if the inne rm is an instance of Monad. That's why you cannot have an unconstrained m when using the Monad instance of ReaderT (which your FooM type is using via the deriving mechanism).
returns type is Monad m => a -> m a, hence the need for the constraint.
By the monad laws, foo ≡ ask, which will indeed work without the Monad constraint. But if you don't require Monad, then GHC can hardly make simplifications based on these laws, can it? Certainly not before type checking the code. And what you wrote is syntactic sugar for
foo = ask >>= \x -> return x
which requires both (>>=) :: Monad (FooM m) => FooM m Int -> (Int->FooM m Int) -> FooM m Int and return :: Monad (FooM m) => Int->FooM m Int.
Again, the >>= return does nothing whatsoever for a correct monad, but for a non-monad it isn't even defined and can thus not just be ignored.
Related
So I've been playing around with MonadState class and I have encountered something I consider very strange.
I can try to write a monad like the following:
test ::
( MonadState Int m
, MonadState Bool m
)
=> m ()
test = do
((+1) <$> get) >>= put
(not <$> get) >>= put
If we compile this in ghc 8.6.4 we get the following:
MonadTrans.hs:10:13: error:
• Couldn't match type ‘Int’ with ‘Bool’
arising from a functional dependency between constraints:
‘MonadState Bool m’
arising from a use of ‘get’ at MonadTrans.hs:10:13-15
‘MonadState Int m’
arising from the type signature for:
test :: forall (m :: * -> *).
(MonadState Int m, MonadState Bool m) =>
m ()
at MonadTrans.hs:(4,1)-(8,11)
• In the second argument of ‘(<$>)’, namely ‘get’
In the first argument of ‘(>>=)’, namely ‘((+ 1) <$> get)’
In a stmt of a 'do' block: ((+ 1) <$> get) >>= put
|
10 | ((+1) <$> get) >>= put
|
(older versions of GHC for example 8.2.2 are actually fine with this and compile. I have no idea why.)
Ok this makes sense since the declaration of MonadState has a dependency in it:
class Monad m => MonadState s m | m -> s where
we cannot have a single Monad be both MonadState Int and MonadState Bool. But here is where things get a little strange.
If I add a type annotation the code will compile
test ::
( MonadState Int m
, MonadState Bool m
)
=> m ()
test = do
(((+1) :: Int -> Int) <$> get) >>= put
(not <$> get) >>= put
To me this seems very strange. A moment ago it was complaining about a very real functional dependency conflict between the two. I don't see how disambiguating the type of (+1) makes that conflict go away.
What is happening here? How does the second one compile while the first fails? And why does the first compile on 8.2.2?
Try this:
plus1 :: Int -> Int
plus1 = (+ 1)
test :: (MonadState Int m, MonadState Bool m) => m ()
test = do
(plus1 <$> get) >>= put
(not <$> get) >>= put
Compiles fine, even without the inline type annotation.
What the functor?!
The thing is, when the compiler complains in your first example, it doesn't complain about the type signature just because it decided to verify it for the heck of it. Look a bit further in the error message: ...In the second argument of ‘(<$>)’, namely ‘get’...
Aha! The source of trouble is actually get! But why?
The trouble is the bloody overloaded arithmetic. You see, operator (+) has a polymorphic type, like this:
(+) :: Num a => a -> a -> a
And naked literals also have similar type:
1 :: Num a => a
So when you write (+1), it doesn't let the compiler know that you meant Int. It admits any type a as long as there is Num a.
So the compiler turns to further surroundings to get the type. But wait! Further surroundings are also generic:
get :: MonadState a m => m a
put :: MonadState a m => a -> m ()
Ok, so maybe we can get the type from the signature of test? Let's check that! Oh, no, the signature actually contains a conflict! Bail, bail, bail! That's when you get the error.
All of this doesn't happen on the second line, because not has a non-polymorphic type not :: Bool -> Bool, so the required type of get is known. And this is why either giving an inline type annotation Int -> Int or having it come from an external function plus1 helps on the first line as well.
If you do provide enough type information for the values in the body, the compiler never has to analyze the test signature. The signature specifies that there should be a MonadState Int m dictionary, and that's good enough. Whoever calls the function will have provide the dictionary, and we'll just use that.
Now, of course, when you get around to calling this function, you'll need to provide both dictionaries MonadState Int m and MonadState Bool m, and you can't get those, so you can't actually call such function. But you sure can define it.
That being said, you CAN actually have a monad with two different MonadState instances if you're willing to be sneaky enough about it.
Of course, if you try it straight up, you get a very straight up error:
data M a = M
instance MonadState Int M
instance MonadState Bool M
> Functional dependencies conflict between instance declarations:
> instance MonadState Int M -- Defined at ...
> instance MonadState Bool M -- Defined at ...
Ok, let's start small:
data M a = M
instance MonadState Int M
> Illegal instance declaration for `MonadState a M'
> The liberal coverage condition fails in class `MonadState'
> for functional dependency: `m -> s'
> Reason: lhs type `M' does not determine rhs type `a'
> Un-determined variable: a
Alright, so something in the type of M must indicate the type Int. That makes sense. Let's add it:
data M x a = M a
instance MonadState Int (M Int)
Ok, this works. So far so good.
But of course, in order to define MonadState Bool, I need to add Bool to the type as well:
data M x y a = M a
instance MonadState Int (M Int y)
instance MonadState Bool (M x Bool)
> Functional dependencies conflict between instance declarations:
Ah, still fundep failure! Ok, well, that makes sense too.
So is there a way I can fool the compiler into not checking the instances for the fundep? Yes, there is! I can be sneaky and make the instances overlapped, like this:
instance {-# OVERLAPPABLE #-} (Num a, Show a) => MonadState a (M a y) where
get = M 42
put x = M ()
instance {-# OVERLAPPING #-} MonadState Bool (M x Bool) where
get = M True
put x = M ()
Now all that's left is the Monad instance, and we can have it all actually run:
data M x y a = M a deriving (Functor, Show)
instance Applicative (M x y) where
pure = M
(M f) <*> (M x) = M $ f x
instance Monad (M x y) where
(M x) >>= f = f x
instance {-# OVERLAPPABLE #-} (Num a, Show a) => MonadState a (M a y) where
get = M 42
put x = trace ("Setting Num: " ++ show x) $ M ()
instance {-# OVERLAPPING #-} MonadState Bool (M x Bool) where
get = M True
put x = trace ("Setting Bool: " ++ show x) $ M ()
g :: M Int Bool ()
g = test
main = print g
I've included debug trace to verify how they're actually going to work, so the above program prints:
Setting Num: 43
Setting Bool: False
M ()
How do I create a monad which uses State, Cont, and Reader transformers? I would like to read an environment, and update/use state. However, I would also like to pause/interrupt the action. For example, if a condition is met, the state remains unchanged.
So far I have a monad using ReaderT and StateT, but I cannot work out how to include ContT:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Test where
-- monads
import Data.Functor.Identity (Identity, runIdentity)
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Cont
-- reader environment
type In = Integer
-- cont: if true then pause, else continue
type Pause = Bool
-- state environment:
newtype StateType = StateType { s :: Integer }
newtype M r = M {_unM :: ReaderT In (ContT Pause (StateT StateType Identity)) r}
deriving ( Functor, Applicative, Monad
, MonadReader In
, MonadCont Pause
, MonadState StateType
)
-- run monadic action
runM :: In -> Pause -> StateType -> M r -> StateType
runM inp pause initial act
= runIdentity -- unwrap identity
$ flip execStateT initial -- unwrap state
$ flip runContT pause -- unwrap cont
$ flip runReaderT inp -- unwrap reader
$ _unM act -- unwrap action
This gives the error:
* Expected kind `* -> *', but `Pause' has kind `*'
* In the first argument of `MonadCont', namely `Pause'
In the newtype declaration for `M'
|
24| , MonadCont Pause
|
Ok, but why does Pause need kind * -> *?... I'm drowning in types, in need of explanation. What form does Pause have to take, a function? How does ContT integrate? Ultimately, I plan to use Cont for a control structure.
Unlike MonadReader and MonadState, the MonadCont type class takes only one parameter. Since that parameter m must be a Monad, it must have kind * -> *.
In your deriving clause, you want MonadCont, not MonadCont Pause.
added in response to followup question:
ContT is defined as:
newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }
Note that the r in your definition of newtype M r is passed as the final (a) parameter to ContT. Plugging in the variables, you have
ContT Bool (State StateType) a = ContT {
runContT :: (a -> State StateType Bool) -> (State StateType Bool)
}
This provides a computational context in which you can manipulate the StateType, and use delimited continuations. Eventually, you will construct a ContT Bool (State StateType) Bool. Then you can run the continuation (with evalContT), and return to the simpler State StateType context. (In practice, you may unwrap all 3 of your monad transformers in the same part of your program.)
The following program compiles under GHC 8.0.2 with no language extensions, and produces the expected two lines of output.
However, it does not compile if the (non-top-level) type declaration for the value write' is removed.
Also, I cannot find any (top-level) type declaration for the function write.
I find this rather odd. If this is acceptable standard Haskell, surely it should be possible to create a type declaration for the function write.
So my question is: is there such a type declaration?
import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
import Control.Monad.Writer (MonadTrans, Writer, lift, runWriter, tell, when)
import ListT (ListT, toList) -- Volkov's list-t package
logging = True
write x = when logging write' where
write' :: MonadTrans m => m (Writer [String]) ()
write' = lift $ tell [x]
f :: ListT (Writer [String]) String
f = do
write "Hello from f"
return "ABC"
g :: MaybeT (Writer [String]) Int
g = do
write "Hello from g"
return 123
main :: IO ()
main = do
print $ runWriter $ toList f
print $ runWriter $ runMaybeT g
Using GHCi (remember to put this into a separate file and load it on GHCi's command line lest you get confused by GHCi's altered typing rules):
> :t write
write :: (Applicative (m (Writer [String])), MonadTrans m) =>
String -> m (Writer [String]) ()
Why? Well,
write' :: MonadTrans m => m (Writer [String]) ()
when :: Applicative f => Bool -> f () -> f ()
when logging :: Applicative f => f () -> f ()
so, when logging write' must unify write''s m (Writer [String]) with when loggings's f, causing the combined constraint (Applicative (m (Writer [String])), MonadTrans m). But wait, let's remove the type signatures and see what the most general type is:
-- equivalent but slightly easier to talk about
write = when logging . lift . tell . (:[])
(:[]) :: a -> [a]
tell :: MonadWriter w m -> w -> m ()
lift :: (Monad m, MonadTrans t) => m a -> t m a
tell . (:[]) :: MonadWriter [a] m => a -> m ()
lift . tell . (:[]) :: (MonadWriter [a] m, MonadTrans t) => a -> t m ()
when logging . lift . tell . (:[]) = write
:: (Applicative (t m), MonadWriter [a] m, MonadTrans t) => a -> t m ()
-- GHCi agrees
Per se, there's nothing wrong with this type. However, standard Haskell does not allow this. In standard Haskell, a constraint must be of the form C v or C (v t1 t2 ...) where v is a type variable. In the compiling case, this holds: the Applicative constraint has the type variable m on the outside, and the MonadTrans is just m. This is true in the non-compiling version, too, but we also have the constraint MonadWriter ([] a) m. [] is no type variable, so the type here is rejected. This constraint arises in the compiling version, too, but the type signatures nail the variables down to produce MonadWriter [String] (Writer [String]), which is immediately satisfied and does not need to appear in the context of write.
The restriction is lifted by enabling FlexibleContexts (preferably via a {-# LANGUAGE FlexibleContexts #-} pragma, but also maybe by -XFlexibleContexts). It originally existed to prevent things such as the following:
class C a where c :: a -> a
-- no instance C Int
foo :: C Int => Int
foo = c (5 :: Int)
-- with NoFlexibleContexts: foo's definition is in error
-- with FlexibleContexts: foo is fine; all usages of foo are in error for
-- not providing C Int. This might obscure the source of the problem.
-- slightly more insiduous
data Odd a = Odd a
-- no Eq (Odd a)
oddly (Odd 0) (Odd 0) = False
oddly l r = l == r
-- oddly :: (Num a, Eq (Odd a), Eq a) => Odd a -> Odd a -> Bool
-- Now the weird type is inferred! With FlexibleContexts,
-- the weird constraint can propagate quite far, causing errors in distant
-- places. This is confusing. NoFlexibleContexts places oddly in the spotlight.
But it happens to get in the way a lot when you have MultiParamTypeClasses on.
How does Haskell know which is correct monad instance for each return expression?
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . return
It's actually unambiguous from the context.
Let's play the typechecker,
-- From the signature
MaybeT . return . return :: a -> MaybeT a
-- From the type of MaybeT
return . return :: a -> m (Maybe a)
-- From the type of `.`
(return :: Maybe a -> m a) . (return :: a -> Maybe a)
And once we have the type of each return, the "instance selection algorithm" will correctly choose the first to use ms return and the second to be Maybe.
It infers the needed types.
It's clear from the meaning of an instance definition that we're trying to define
returnMaybeT :: Monad m => a -> MaybeT m a
returnMaybeT x = MaybeT (return (return x))
Since MaybeT :: m (Maybe a) -> MaybeT a (taken as a function) we know that the inner stack of returns must have type
return (return x) :: Monad m => a -> m (Maybe a)
Now, we know that return is a polymorphic function which has a type like
return :: a -> n a
for any Monad n. In the case of this first return, the Monad m => constraint tells us that m is a Monad and so we can use its definition of return. This lets us get all the way down to the inner return
return x :: a -> Maybe a
and since we know that Maybe has a Monad instance we can use the return from that.
Ultimately, all the compiler has to do is whittle its way down the expression trying to determine the types needed at each return. After it determines the needed type it has to check to see if it knows a Monad instance for that type. This is simple for Maybe, since it's concrete, but a little more difficult to see for m since it's just a variable.
The reason m works is because we've constrained the variable to certainly be some type which instantiates Monad.
Consider the following example program:
next :: Int -> Int
next i
| 0 == m2 = d2
| otherwise = 3 * i + 1
where
(d2, m2) = i `divMod` 2
loopIteration :: MaybeT (StateT Int IO) ()
loopIteration = do
i <- get
guard $ i > 1
liftIO $ print i
modify next
main :: IO ()
main = do
(`runStateT` 31) . runMaybeT . forever $ loopIteration
return ()
It can only use get instead of lift get because instance MonadState s m => MonadState s (MaybeT m) is defined in the MaybeT module.
Many such instances are defined in kind of a combinatoric explosion manner.
It would have been nice (although impossible? why?) if we had the following type-class:
{-# LANGUAGE MultiParamTypeClasses #-}
class SuperMonad m s where
lifts :: m a -> s a
Let's try to define it as such:
{-# LANGUAGE FlexibleInstances, ... #-}
instance SuperMonad a a where
lifts = id
instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b) where
lifts = lift . lifts
Using lifts $ print i instead of liftIO $ print i works, which is nice.
But using lifts (get :: StateT Int IO Int) instead of (get :: MaybeT (StateT Int IO) Int) doesn't work.
GHC (6.10.3) gives the following error:
Overlapping instances for SuperMonad
(StateT Int IO) (StateT Int IO)
arising from a use of `lifts'
Matching instances:
instance SuperMonad a a
instance (SuperMonad a b, MonadTrans t, Monad b) =>
SuperMonad a (t b)
In a stmt of a 'do' expression:
i <- lifts (get :: StateT Int IO Int)
I can see why "instance SuperMonad a a" applies. But why does GHC think that the other one does, too?
To follow up ephemient's excellent answer: Haskell type classes use an open-world assumption: some idiot can come along later and add an instance declaration that's not a duplicate and yet overlaps with your instance. Think of it as an adversary game: if an adversary can make your program ambiguous, the compiler bleats.
If you're using GHC you can of course say to the compiler "to hell with your paranoia; allow me my ambiguous instance declaration":
{-# LANGUAGE OverlappingInstances #-}
If later evolution of your program leads to overload resolution you didn't expect, the compiler gets 1,000 I-told-you-so points :-)
Deprecation Note
This pragma has been deprecated since GHC 7.10, and per-instance pragmas should be used instead. More detail can be found in the GHC documentation.
Just because you haven't defined an instance in your current module doesn't mean that one couldn't be defined somewhere else.
{-# LANGUAGE ... #-}
module SomeOtherModule where
-- no practical implementation, but the instance could still be declared
instance SuperMonad (StateT s m) m
Suppose your module and SomeOtherModule are linked together in a single program.
Now, answer this: does your code use
instance SuperMonad a a
-- with a = StateT Int IO
or
instance (SuperMonad a b, MonadTrans t, Monad b) => SuperMonad a (t b)
-- with a = StateT Int IO
-- t = StateT Int
-- b = IO
?
When you have overlapping instances try to attach their behaviour to newtypes:
type SuperEgo :: (k -> Type) -> (k -> Type)
newtype SuperEgo m a = SuperEgo (m a)
type Elevator :: (k -> k1 -> Type) -> (k -> k1 -> Type)
newtype Elevator trans m a = Elevator (trans m a)
instance SuperMonad m (SuperEgo m) where
lifts :: m ~> SuperEgo m
lifts = SuperEgo
instance (SuperMonad m super, Monad super, MonadTrans trans) => SuperMonad m (Elevator trans super) where
lifts :: m ~> Elevator trans super
lifts = Elevator . lift . lifts
Monads can now derive via SuperEgo M to get an identity instances
{-# Language DerivingVia #-}
data Ok a = Ok a
deriving (SuperMonad Ok)
via SuperEgo Ok
It's more of a hassle to define a monad transformer so I'll show how to define a lifting instance for an existing Monad transformers like StateT s. This uses standalone deriving which is more verbose, you need to fill in the class context yourself:
deriving
via Elevator (StateT s) super
instance (Monad super, SuperMonad m super) => SuperMonad m (StateT s super)