You can get a lot of this information through GHCi. That is, if you happen to know the various incantations and how to interpret them. Let's start with MonadState
:
ghci> :m + Control.Monad.State
ghci> :i MonadState
type MonadState :: * -> (* -> *) -> Constraint
class Monad m => MonadState s m | m -> s where
get :: m s
put :: s -> m ()
state :: (s -> (a, s)) -> m a
{-# MINIMAL state | get, put #-}
-- Defined in ‘Control.Monad.State.Class’
instance [safe] Monad m => MonadState s (StateT s m)
-- Defined in ‘Control.Monad.State.Class’
Ignoring the missing instance for your type for now, we can see that MonadState
defines three operations (the CRUD-operations if you will). We can further see that StateT
has an instance of MonadState
. This means that we can specialise e.g. get
:
ghci> :t get @_ @(StateT _ _)
get @_ @(StateT _ _) :: Monad _1 => StateT _2 _1 _2
So we require that the second parameter to StateT
must be a monad. This of course matches the instance we saw above. Now you chose to write a newtype
-wrapper around StateT SomeList IO
which means that you don't have a MonadState
instance. You could solve this in two ways:
- Use a type synonym in stead of a
newtype
- Implement an instance. You can derive such an instance with
-XGeneralizedNewtypeDeriving
Assuming you create an instance instance MonadState SomeList SomeListStateT
you would then see that the three functions specialise to:
ghci> :t get @_ @SomeListStateT
get @_ @SomeListStateT :: SomeListStateT SomeList
ghci> :t put @_ @SomeListStateT
put @_ @SomeListStateT :: SomeList -> SomeListStateT ()
ghci> :t state @_ @SomeListStateT
state @_ @SomeListStateT
:: (SomeList -> (a, SomeList)) -> SomeListStateT a
If you want to retain your newtype wrapper, you should consider writing a function analogous to:
ghci> :t runStateT
runStateT :: StateT s m a -> s -> m (a, s)
But with the type
SomeListStateT a -> SomeList -> IO (a, SomeList)