Reputation: 141
I'm writing a haskell program to execute a bunch of statements to modify a data record. I would like to make modifications and tests to the state at each statement without user intervention. I had the idea to modify the Control.Monad.Trans.State.Strict module to incorporate my specific type in a tuple (,)
.
It worked well until I had the following error
• Couldn't match type ‘a’ with ‘b’
‘a’ is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b.
StateT s m a -> (a -> StateT s m b) -> StateT s m b
at TestMon.hs:38:7
‘b’ is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b.
StateT s m a -> (a -> StateT s m b) -> StateT s m b
at TestMon.hs:38:7
Expected type: m (b, s, LogS)
Actual type: m (a, s, LogS)
and I don't understand why I get this error. My code is like this :
module TestStateJLJ1 where
import Data.Functor.Identity
import Control.Applicative
import Control.Monad
data LogS = LogOK { stepS:: Int ,logS::[String] }
| LogErr {stepS:: Int , logS::[String] }
deriving (Show)
initLogS = LogOK { stepS=1, logS=[]}
type State s = StateT s Identity
state :: (Monad m) => ((s,LogS) -> (a, s,LogS)) -> StateT s m a
state f = StateT (return . f )
newtype StateT s m a = StateT { runStateT :: (s,LogS) -> m (a,s,LogS) }
instance (Functor m) => Functor (StateT s m) where
fmap f m = StateT $ \ (s,l) ->
fmap (\ (a, s',l') -> (f a, s',l')) $ runStateT m (s,l)
instance (Functor m, Monad m) => Applicative (StateT s m) where
pure a = StateT $ \ (s,l) -> return (a, s,l)
StateT mf <*> StateT mx = StateT $ \ (s,l) -> do
(f, s',l') <- mf (s,l)
(x, s'',l'') <- mx (s',l')
return (f x, s'',l'')
m *> k = m >>= \_ -> k
instance (Monad m) => Monad (StateT s m) where
return a = StateT $ \ (s,l) -> return (a, s,l)
m >>= k = StateT $ \ (s,l) -> do
(a, s',l') <- runStateT m (s,l)
case l' of
LogOK _ _ -> runStateT (k a) (s',l'{stepS=1+stepS l'})
LogErr _ _-> do return ( a, s',l') -- <- This line is causing trouble
fail str = StateT $ \ _ -> fail str
I tried to modify the behaviour of the Monad
instance to test the value of the LogS
data type and according to its value :
Perform an increment of stepS
(to count the number of statement) and many other things (not implemented yet) and continue the monad execution.
Or stop the monad execution and return the actual state.
Do you know what is wrong with my code and how to correct it ?
Upvotes: 3
Views: 414
Reputation: 50829
You need to take a page from other monads that signal errors in the middle of a monadic computation. They don't return
the error value, because that's not generally possible: a monadic action of type m b
can only return
a value of type b
, not some other arbitrary type like (a,s,LogS)
. Instead, these other error-signaling monads use a sum type for representing errors. For example, the monadic action Either MyNastyError b
can only return
a value of type b
(by using Right
), but it can signal an error of type MyNastyError
using Left
.
Also, you won't generally be able to produce an (a,s,LogS)
because not enough is known about the type a
. It's just the return type of some action that was bound into the overall monadic action immediately before the action that ultimately failed, so the caller won't generally be ready to do anything with it. You can return (s,LogS)
though, since that type will be fixed across all actions in a given monad.
Specifically, you can redefine your StateT
type as:
newtype StateT s m a
= StateT { runStateT :: (s, LogS) -> m (Maybe a, s, LogS) }
deriving (Functor)
This uses Maybe a
to signal failure. A computation that returns (Just x, s, l)
can continue, but a (Nothing, s, l)
is ready to stop and dump its state and log.
With this type, the two LogS
constructors are now redundant, because failure is already signaled by the Maybe a
value, so LogS
can be simplified to:
data LogS = LogS { stepS :: Int , logS :: [String] }
deriving (Show)
The appropriate Applicative
and Monad
instances can be updated to:
instance (Monad m) => Applicative (StateT s m) where
pure a = StateT $ \(s, l) -> return (Just a, s, l)
(<*>) = ap
instance (Monad m) => Monad (StateT s m) where
return = pure
m >>= k = StateT $ \(s, l) -> do
(maybeA, s', l') <- runStateT m (s, l)
case maybeA of
Nothing -> return (Nothing, s', l')
Just a -> runStateT (k a) (s', l' {stepS = 1 + stepS l'})
fail err = StateT $ \(s, l) -> return (Nothing, s, l { logS = err:logS l })
Note that I think fail
makes more sense if it does "nice" failure in this monad instead of using the base monad's fail
. I'm assuming here that the logS
field is in reverse order, so latest messages are appended to the head.
The full code:
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE DeriveFunctor #-}
module TestStateJLJ1 where
import Data.Functor.Identity
import Control.Monad
data LogS = LogS { stepS :: Int , logS :: [String] }
deriving (Show)
initLogS :: LogS
initLogS = LogS 1 []
newtype StateT s m a
= StateT { runStateT :: (s, LogS) -> m (Maybe a, s, LogS) }
deriving (Functor)
instance (Monad m) => Applicative (StateT s m) where
pure a = StateT $ \(s, l) -> return (Just a, s, l)
(<*>) = ap
instance (Monad m) => Monad (StateT s m) where
return = pure
m >>= k = StateT $ \(s, l) -> do
(maybeA, s', l') <- runStateT m (s, l)
case maybeA of
Nothing -> return (Nothing, s', l')
Just a -> runStateT (k a) (s', l' {stepS = 1 + stepS l'})
fail err = StateT $ \(s, l) -> return (Nothing, s, l { logS = err:logS l })
type State s = StateT s Identity
state :: (Monad m) => ((s, LogS) -> (Maybe a, s, LogS)) -> StateT s m a
state f = StateT $ return . f
Upvotes: 1