tl;dr How do I get Class to have kind (* -> *) -> Constraint?
I have the following code:
{-# LANGUAGE ConstraintKinds, TypeFamilies, RankNTypes, MultiParamTypeClasses #-}
class MonadTrans t where
type Class t
lift :: (Monad m) => m b -> t m b
generalize :: (Monad m) => t m a -> (forall m'. (Class t m') => m a)
class (Monad m) => MaybeC m where
fart :: m a
data MaybeT m a = NothingT | JustT (m a)
instance (Monad m) => Monad (MaybeT m) where
return = JustT . return
(JustT a) >>= f = a >>= f
NothingT >>= f = NothingT
instance (Monad m) => MaybeC (MaybeT m) where
fart = NothingT
instance MonadTrans MaybeT where
type Class Maybe = MaybeC --Error Is HERE
lift = JustT
generalize NothingT = fart
generalize JustT a = return a
The compiler says: "Expected kind '* -> Constraint', butMaybeC' has kind (* -> *) -> Constraint'</code>" but Class is (*->*)->Constraint.
So then I tried:
{-# LANGUAGE ConstraintKinds, TypeFamilies, RankNTypes, MultiParamTypeClasses #-}
class MonadTrans t where
type Class t :: (*->*)->Constraint
lift :: (Monad m) => m b -> t m b
generalize :: (Monad m) => t m a -> (forall m'. (Class t m') => m a)
class (Monad m) => MaybeC m where
fart :: m a
data MaybeT m a = NothingT | JustT (m a)
instance (Monad m) => Monad (MaybeT m) where
return = JustT . return
(JustT a) >>= f = a >>= f
NothingT >>= f = NothingT
instance (Monad m) => MaybeC (MaybeT m) where
fart = NothingT
instance MonadTrans MaybeT where
type Class Maybe = MaybeC
lift = JustT
generalize NothingT = fart
generalize JustT a = return a
But the compiler says "pad1.hs:4:37: Not in scope: type constructor or class 'Constraint'", so ConstraintKinds is doing nothing for me.
Related
I have a function type declaration
f :: MonadHandler m => SqlPersistT m ()
Which I want to convert to
f :: MonadHandlerDB m => m ()
I try everything I can think of to define constraint MonadHandlerDB, but cannot get either it or function type declaration to compile, e.g.:
class (forall a . (MonadHandler m, m ~ SqlPersistT a)) => MonadHandlerDB m
class MonadHandlerDB m
instance MonadHandler a => MonadHandlerDB (SqlPersistT a)
type MonadHandlerDB m = forall a . (MonadHandler a, m ~ SqlPersistT a)
type MonadHandlerDB = forall a . (MonadHandler a => m ~ SqlPersistT a)
type MonadHandlerDB m = forall a . (MonadHandler a => m ~ SqlPersistT a)
One of the errors:
Couldn't match type `m' with `ReaderT backend0 m0
`m' is a rigid type variable bound by
the type signature for:
f:: forall (m :: * -> *).
MonadHandlerDB m =>
m ()
SqlPersistT is defined as
type SqlPersistT = ReaderT SqlBackend
How do I express this constraint?
I think this achieves what you want:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Control.Monad.Trans.Reader (ReaderT (ReaderT))
import Database.Persist.Sql (SqlPersistT)
import Yesod.Core (MonadHandler)
f :: MonadHandlerDB m => m ()
f = undefined
class (MonadHandler (Sub m), m ~ SqlPersistT (Sub m)) => MonadHandlerDB m where
type Sub m :: * -> *
instance MonadHandler m => MonadHandlerDB (SqlPersistT m) where
type Sub (SqlPersistT m) = m
But note that I think this is really not very good to use in practice. It makes it seem as if the m is completely polymorphic, but, in fact, it can only ever be some monad inside SqlPersistT.
Constraints are powerful, but I think a constraint like this has a high potential to confuse its users.
I am trying to implement a Parsec Stream wrapper that will remember the last uncons'd token in order to provide some look-behind capability. I want the wrapper to work with any Stream instance. Here's what I have so far:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module MStream where
import Text.Parsec
import Control.Monad ( liftM )
data MStream s t = MStream (Maybe t) s
instance Stream s m t => Stream (MStream s t) m t where
uncons (MStream _ s) = fmap (\(t, s') -> (t, MStream (Just t) s')) `liftM` uncons s
getPrevToken :: Stream s m t => ParsecT (MStream s t) u m (Maybe t)
getPrevToken = (\(MStream t _) -> t) `liftM` getInput
mstream :: s -> MStream s t
mstream = MStream Nothing
This works, but I don't like having to carry the t parameter in the MStream type constructor. Surely it should be sufficient to require only the s parameter, since t can be derived from s as long as there is a witness for Stream s m t. I've tried using type families and GADTs, but I keep running into obscure errors about ambiguous type variables and unsatisfied functional dependencies.
Is there a way to remove t from the MStream type constructor so I don't have to write:
sillyParser :: Stream s m Char => ParsecT (MStream s Char) u m String
sillyParser = do
t <- getPrevToken
maybe (string "first") (\c -> string $ "last" ++ [c]) t
With
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module MStream where
import Control.Monad ( liftM )
-- Class so we don't need to carry `m` in `MStream` definition.
class StreamDep s t | s -> t where
class StreamDep s t => Stream s m t where
uncons :: s -> m (Maybe (t, s))
data MStream s = forall t. StreamDep s t => MStream (Maybe t) s
data ParsecT s u m a = ParsecT s u (m a)
instance Monad m => Monad (ParsecT s u m) where
getInput :: ParsecT s u m s
getInput = undefined
instance StreamDep s t => StreamDep (MStream s) t where
instance (Monad m, Stream s m t) => Stream (MStream s) m t where
uncons (MStream _ s) = fmap (\(t, s') -> (t, MStream (Just t) s')) `liftM` uncons s
getPrevToken :: (Monad m, Stream s m t) => ParsecT (MStream s) u m (Maybe t)
getPrevToken = (\(MStream t _) -> t) `liftM` getInput
mstream :: StreamDep s t => s -> MStream s
I get quite close, but get an error:
Pars.hs:28:35:
Could not deduce (t1 ~ t)
from the context (Monad m, Stream s m t)
bound by the type signature for
getPrevToken :: (Monad m, Stream s m t) =>
ParsecT (MStream s) u m (Maybe t)
at Pars.hs:27:17-76
or from (StreamDep s t1)
bound by a pattern with constructor
MStream :: forall s t. StreamDep s t => Maybe t -> s -> MStream s,
in a lambda abstraction
Yet by using both contexts Stream s m t and StreamDep s t1 it should be obvious that (t ~ t1).
By using artillery, we can make it compile:
getPrevToken :: (Monad m, Stream s m t) => ParsecT (MStream s) u m (Maybe t)
getPrevToken = (\(MStream t _) -> unsafeCoerce t) `liftM` getInput
But I cannot try this, as it requires modification to parsec.
So, I solved this by moving the goalposts (somewhat):
{-# LANGUAGE FlexibleContexts, FlexibleInstances,
MultiParamTypeClasses, FunctionalDependencies #-}
module MStream where
import Text.Parsec
import Control.Monad ( liftM )
class Stream s m t => MStream s m t | s -> t where
getPrevToken :: ParsecT s u m (Maybe t)
data MStreamImpl s t = MStreamImpl (Maybe t) s
instance Stream s m t => MStream (MStreamImpl s t) m t where
getPrevToken = (\(MStreamImpl t _) -> t) `liftM` getInput
instance Stream s m t => Stream (MStreamImpl s t) m t where
uncons (MStreamImpl _ s) = fmap (\(t, s') -> (t, MStreamImpl (Just t) s')) `liftM` uncons s
mstream :: s -> MStreamImpl s t
mstream = MStreamImpl Nothing
sillyParser :: MStream s m Char => ParsecT s u m String
sillyParser = do
t <- getPrevToken
maybe (string "first") (\c -> string $ "last" ++ [c]) t
Instead of trying to remove the type parameter from MStream, I turned MStream into a typeclass, with a canonical instance MStreamImpl. So now, the sillyParser type signature can be written in a more compact manner simply by replacing the Stream context with an MStream context.
I've defined my own monad transformer:
data Config = Config { ... }
data State = State { ... }
newtype FooT m a = FooT {
runFoo :: ReaderT Config (StateT State m) a
} deriving (Functor, Monad, MonadReader Config, MonadState State)
And I've defined a MonadTrans instance for it.
instance MonadTrans FooT where
lift = FooT . lift . lift
Now, I have various monads that I can't just have be derived for me by the compiler. I'll take MonadIO as the example. So I've defined my MonadIO instance as
instance MonadIO m => MonadIO (FooT m) where
liftIO = lift . liftIO
However, I find that I'm doing a lot of lifting, for each Monad. Why could the author of each Monad typeclass (i.e. MonadIO, MonadCatchIO, MonadFoo) not define a general instance in terms of MonadTrans, instead of making me implement an instance for each new MonadTrans I come up with? a la
instance (MonadIO m, MonadTrans t, Monad (t m)) => MonadIO (t m) where
liftIO = lift . liftIO
That requires UndecidableInstances to compile, and I'm not certain that it's correct (in fact, pretty sure it's incorrect), but serves to express my intent for now.
So, is this possible? If not, why not? Will it ever be?
Let's say that I've come up with an alternative to MonadIO, called
MyMonadIO. It's like MonadIO in every way, except for the name:
class Monad m => MyMonadIO m where
myLiftIO :: IO a -> m a
Assuming your FooT type:
newtype FooT m a = FooT
{ runFoo :: ReaderT Config (StateT AppState m) a
} deriving (Functor, Applicative, Monad, MonadReader Config, MonadState AppState)
It's possible to create an instance of MyMonadIO for ReaderT,
StateT, and finally FooT. I've added extra type annotations to make it
easier for the reader to figure out what's going on:
instance MyMonadIO m => MyMonadIO (ReaderT r m) where
myLiftIO :: IO a -> ReaderT r m a
myLiftIO = (lift :: m a -> ReaderT r m a) . (myLiftIO :: IO a -> m a)
instance MyMonadIO m => MyMonadIO (StateT s m) where
myLiftIO :: IO a -> StateT s m a
myLiftIO = (lift :: m a -> StateT s m a) . (myLiftIO :: IO a -> m a)
instance MyMonadIO m => MyMonadIO (FooT m) where
myLiftIO :: IO a -> FooT m a
myLiftIO = (lift :: m a -> FooT m a) . (myLiftIO :: IO a -> m a)
It's also possbile to use GeneralizedNewtypeDeriving to easily derive
MyMonadIO for FooT (assuming there are already instances for ReaderT and
StateT):
newtype FooT m a = FooT
{ runFoo :: ReaderT Config (StateT AppState m) a
} deriving (Functor, Applicative, Monad, MyMonadIO, MonadReader Config, MonadState AppState)
If you look at the body of the myLiftIO function for the ReaderT, StateT,
and FooT instances, they are exactly the same: lift . myLiftIO.
Here's a repeat of the question:
Why could the author of each Monad typeclass (i.e. MonadIO, MonadCatchIO,
MonadFoo) not define a general instance in terms of MonadTrans, instead of
making me implement an instance for each new MonadTrans I come up with?
For MyMonadIO, this general instance would be as follows:
instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n) where
myLiftIO :: IO a -> t n a
myLiftIO = (lift :: n a -> t n a) . (myLiftIO :: IO a -> n a)
With this instance defined, you don't need a specific instance for ReaderT,
StateT, or even FooT.
This requires UndecidableInstances. However, the problem with this is not undecidability, but that this instance overlaps some potentially valid instances of MyMonadIO.
For instance, imagine the following datatype:
newtype FreeIO f a = FreeIO (IO (Either a (f (FreeIO f a))))
instance Functor f => Functor (FreeIO f) where
fmap :: (a -> b) -> FreeIO f a -> FreeIO f b
fmap f (FreeIO io) = FreeIO $ do
eitherA <- io
pure $
case eitherA of
Left a -> Left $ f a
Right fFreeIO -> Right $ fmap f <$> fFreeIO
instance Functor f => Applicative (FreeIO f) where
pure :: a -> FreeIO f a
pure a = FreeIO . pure $ Left a
(<*>) :: FreeIO f (a -> b) -> FreeIO f a -> FreeIO f b
(<*>) (FreeIO ioA2b) (FreeIO ioA) = FreeIO $ do
eitherFa2b <- ioA2b
eitherFa <- ioA
pure $
case (eitherFa2b, eitherFa) of
(Left a2b, Left a) -> Left $ a2b a
(Left a2b, Right fFreeIOa) -> Right $ fmap a2b <$> fFreeIOa
(Right fFreeIOa2b, o) -> Right $ (<*> FreeIO (pure o)) <$> fFreeIOa2b
instance Functor f => Monad (FreeIO f) where
(>>=) :: FreeIO f a -> (a -> FreeIO f b) -> FreeIO f b
(>>=) (FreeIO ioA) mA2b = FreeIO $ do
eitherFa <- ioA
case eitherFa of
Left a ->
let (FreeIO ioB) = mA2b a
in ioB
Right fFreeIOa -> pure . Right $ fmap (>>= mA2b) fFreeIOa
You don't necessarily need to understand this FreeIO datatype (especially the Functor, Applicative, and Monad instances). It's enough just to know that this is a valid data type.
(If you're interested, this is just a free monad wrapped around IO.)
It's possible to write a MyMonadIO instance for FreeIO:
instance Functor f => MyMonadIO (FreeIO f) where
myLiftIO :: IO a -> FreeIO f a
myLiftIO ioA = FreeIO (Left <$> ioA)
We can even imagine writing a function using FreeIO:
tryMyLiftIOWithFreeIO :: Functor f => FreeIO f ()
tryMyLiftIOWithFreeIO = myLiftIO $ print "hello"
If you try to compile tryMyLiftIOWithFreeIO with both this instance (MyMonadIO (FreeIO f)) and the bad instance from above, you get the following error:
test-monad-trans.hs:103:25: error:
• Overlapping instances for MyMonadIO (FreeIO f)
arising from a use of ‘myLiftIO’
Matching instances:
instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n)
-- Defined at test-monad-trans.hs:52:10
instance Functor f => MyMonadIO (FreeIO f)
-- Defined at test-monad-trans.hs:98:10
• In the expression: myLiftIO $ print "hello"
In an equation for ‘tryMyLiftIOWithFreeIO’:
tryMyLiftIOWithFreeIO = myLiftIO $ print "hello"
Why does this happen?
Well, in instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n), what is the kind of t and n?
Since n is supposed to be a Monad, it's kind is * -> *. And since t is a monad transformer, it's kind is (* -> *) -> * -> *. t n is also supposed to be a Monad, so it's kind is also * -> *:
n :: * -> *
t :: (* -> *) -> * -> *
t n :: * -> *
Now, in instance Functor f => MyMonadIO (FreeIO f), what are the kinds of FreeIO and f?
f is supposed to be a Functor, so it's kind is * -> *. FreeIO's kind is (* -> *) -> * -> *. FreeIO f is a Monad, so it's kind is * -> *:
f :: * -> *
FreeIO :: (* -> *) -> * -> *
FreeIO f :: * -> *
Since the kinds are the same, you an see that instance Functor f => MyMonadIO (FreeIO f) overlaps with instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n). GHC isn't sure which one to pick!
You can get around this by marking your instance FreeIO instance as OVERLAPPING:
instance {-# OVERLAPPING #-} Functor f => MyMonadIO (FreeIO f) where
myLiftIO :: IO a -> FreeIO f a
myLiftIO m = FreeIO (Left <$> m)
However, this is a treacherous route to go down. You can find out more about why overlapping can be bad from the GHC user guide.
This FreeIO example was created by Edward Kmett. You can find another clever example of an overlapping instance in this reddit post.
If you are planning on writing a monad typeclass (like MyMonadIO) and
releasing it to Hackage, one option is to use the
DefaultSignatures
functionality. This makes it easier for users of your library to define
instances.
Using DefaultSignatures, defining the MyMonadIO class would look like this:
class Monad m => MyMonadIO m where
myLiftIO :: IO a -> m a
default myLiftIO
:: forall t n a.
( MyMonadIO n
, MonadTrans t
, m ~ t n
)
=> IO a -> t n a
myLiftIO = (lift :: n a -> t n a) . (myLiftIO :: IO a -> n a)
This says that there is a default implementation of myLiftIO for any t n,
where n is an instance of MyMonadIO, and t is an instance of
MonadTrans.
With this default siguature for myLiftIO, defining instances of MyMonadIO for ReaderT and StateT would look like this:
instance MyMonadIO m => MyMonadIO (ReaderT r m)
instance MyMonadIO m => MyMonadIO (StateT s m)
Very simple. You don't need to provide the function body of myLiftIO since
it will use the default.
The only drawback of this is that it is not widely done. The
DefaultSignatures machinery seems to be mainly used for generic
programming, not monad typeclasses.
I know with the data constructor and the run*** function,
I can lift any function to a specific MonadTrans instance.
Like this,
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
import Control.Monad
liftF :: (Monad m) => (a -> b) -> MaybeT m a -> MaybeT m b
liftF f x = MaybeT $ do
inner <- runMaybeT x
return $ liftM f inner
But how can I generalize this liftF to
liftF :: (MonadTrans t, Monad m) => (a -> b) -> t m a -> t m b
As #thoferon mentioned, you can just use liftM:
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
import Control.Monad (liftM)
liftF :: (Monad m) => (a -> b) -> MaybeT m a -> MaybeT m b
liftF f m = liftM f m
liftF' :: (MonadTrans t, Monad m, Monad (t m)) => (a -> b) -> t m a -> t m b
liftF' f m = liftM f m
(I had to add an additional Monad constraint to liftF').
But why would you do this? Check out the source code for MaybeT -- there's already a Monad instance:
instance (Monad m) => Monad (MaybeT m) where
fail _ = MaybeT (return Nothing)
return = lift . return
x >>= f = MaybeT $ do
v <- runMaybeT x
case v of
Nothing -> return Nothing
Just y -> runMaybeT (f y)
And actually, as liftM is the same as Functor's fmap:
instance (Functor m) => Functor (MaybeT m) where
fmap f = mapMaybeT (fmap (fmap f))
You can find similar instances for all the transformers.
Is this what you are asking? Can you provide some more concrete examples that show what you're trying to do and why, and in what way the existing Functor and Monad instances fail to meet your needs?
I want to set the type of the state parameter, of a state monad tranformer, to an associated type of that monad transformer. However, this results in constructing an infinite type,
s = AssocTyp (StateT s m) a
The intuition as to why this isn't really a problem is that,
AssocTyp (StateT s m) a = AssocTyp (StateT s' m) a
for all s and s'. However, the compiler isn't smart enough to figure this out. I've read that in some cases, a newtype can be used to avoid infinite types; how do I do it?
Here's minimized code to reproduce the question,
{-# LANGUAGE KindSignatures, TypeFamilies #-}
import Control.Monad.Trans.State
class MyMonad (m :: * -> *) where
data AssocTyp m :: * -> *
instance MyMonad m => MyMonad (StateT s m) where
data AssocTyp (StateT s m) a = StateTA (AssocTyp m a)
isAssocTyp :: Monad m => (AssocTyp m a) -> m ()
isAssocTyp x = return ()
x = do
v <- get
isAssocTyp (v)
I'm not sure what you're trying to achieve. However, the equality you state,
AssocTyp (StateT s m) a = AssocTyp (StateT s' m) a
is not true, because you are using a data family rather than a type family. The following code compiles:
{-# LANGUAGE KindSignatures, TypeFamilies #-}
import Control.Monad.Trans.State
class MyMonad (m :: * -> *) where
type AssocTyp m :: * -> *
instance MyMonad m => MyMonad (StateT s m) where
type AssocTyp (StateT s m) = AssocTyp m
isAssocTyp :: Monad m => (AssocTyp m a) -> m ()
isAssocTyp x = return ()
x :: Monad m => StateT (AssocTyp m a) m ()
x = do
v <- get
isAssocTyp v
The difference between type families and data families is that data families are injective, which means the following implication holds if DF is data family:
DF a b c = DF a' b' c' =====> a = a', b = b', c = c'
This is not what you want in your case.
Just in case a data family is what you want, I have a variant that does type check:
isAssocType' :: (Monad m1, Monad m0) => (AssocTyp m1 a) -> m0 ()
isAssocType' _ = return ()