Search code examples
haskellmonadsghcmonad-transformersstate-monad

How to implement the MonadState class without using record syntax?


I find it difficult for me to understand the MonadState .

The reason maybe most of the examples mixing up with record syntax in their data structure.

So, I tried to implement the MonadState without using record syntax.

The following code I wrote did pass the compiler, but it seems totally nonsense to me.

What is wrong with these code?

Is there a simple example of implementing MonadState without using record syntax?

data Foo a b = Foo (Maybe ([a],b)) deriving (Show)

unwrapFoo :: Foo a b -> Maybe ([a],b)
unwrapFoo (Foo x) = x

instance Monad (Foo [a]) where
  return x = Foo $ Just ([], x) 
  m >>= f  = case unwrapFoo m of
               Just (_, r) -> f r
               Nothing     -> Foo Nothing 

instance MonadState Int (Foo [a]) where
  get   = Foo $ Just ([], 1)     
  put _ = Foo $ Just ([],())


*Main> get :: Foo [a] Int
Foo (Just ([],1))
*Main> put 3 :: Foo [a] ()
Foo (Just ([],()))
*Main>

Solution

  • So let's start with the basic idea of the State Monad.

    newtype MyState s a = MyState (s {- current state -}
                               -> (s {- New state -}, a {- New value -}))
    
    unwrap (MyState f) = f
    

    So now we need to implement >>= and return.

    return is pretty easy:

    return a = MyState $ \s -> -- Get the new state
                         (s, a) -- and pack it into our value
    

    In other words, this just passes the current state through with a new value.

    And now >>=

    (MyState f) >>= g = MyState $ \state -> 
        let (newState, val) = f state
            MyState newF    = g val
        in newF state
    

    So we get a new state, feed it into our existing state monad, then pass the resulting value/state pair into g and return the result of that.

    The total number of differences between this and the record syntax is just that I had to manually define unwrap.

    To complete our monad

    runState = unwrap
    
    get = MyState \s -> (s, s)
    put a = MyState \s -> (a, ())