Vigo
Vigo

Reputation: 141

Couldn't match type ‘a’ with ‘b’error in Monad instance definition

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 :

Do you know what is wrong with my code and how to correct it ?

Upvotes: 3

Views: 414

Answers (1)

K. A. Buhr
K. A. Buhr

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

Related Questions