3

I am trying to create a monad that combines state and error processing, like this

import Control.Monad

data Result a e = Ok a | Error e

newtype StateError s e a = StateError { runStateError :: s -> (Result a e, s) }

instance Monad (StateError s e) where
  return x = StateError $ \s -> (Ok x, s)

  m >>= f = StateError $
    \s -> case runStateError m s of
            (Ok x, s') -> runStateError (f x) s'
            e -> e

get = StateError $ \s -> ((Ok s), s)

put s = StateError $ \_ -> ((Ok ()), s)

main = return ()

When I compile, I receive this error, which I do not know how to fix:

StateError.hs:13:18: error:
    • Couldn't match type ‘a’ with ‘b’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          (>>=) :: forall a b.
                   StateError s e a -> (a -> StateError s e b) -> StateError s e b
        at StateError.hs:10:5-7
      ‘b’ is a rigid type variable bound by
        the type signature for:
          (>>=) :: forall a b.
                   StateError s e a -> (a -> StateError s e b) -> StateError s e b
        at StateError.hs:10:5-7
      Expected type: (Result b e, s)
        Actual type: (Result a e, s)
    • In the expression: e
      In a case alternative: e -> e
      In the expression:
        case runStateError m s of
          (Ok x, s') -> runStateError (f x) s'
          e -> e
    • Relevant bindings include
        e :: (Result a e, s) (bound at StateError.hs:13:13)
        f :: a -> StateError s e b (bound at StateError.hs:10:9)
        m :: StateError s e a (bound at StateError.hs:10:3)
        (>>=) :: StateError s e a
                 -> (a -> StateError s e b) -> StateError s e b
          (bound at StateError.hs:10:5)
   |
13 |             e -> e
   |                  ^

What am I doing wrong here? I think the problem is the difficulty of matching the two results of the case

      Expected type: (Result b e, s)
      Actual type: (Result a e, s)

like force a to be a b, or something alike, but I don't know how to solve this.

In addition, I am also receiving this error:

StateError.hs:7:10: error:
    • No instance for (Applicative (StateError s e))
        arising from the superclasses of an instance declaration
    • In the instance declaration for ‘Monad (StateError s e)’
  |
7 | instance Monad (StateError s e) where
  |          ^^^^^^^^^^^^^^^^^^^^^^

This asks me to instantiate Applicative, so I would appreciate some guidance on what to do here.

Thanks

Will Ness
  • 62,652
  • 8
  • 86
  • 167
mljrg
  • 3,545
  • 1
  • 27
  • 42
  • 1
    about the Applicative, it must be defined too. you just put your `return` into the Applicative instance under the name `pure`, and add `a b = ap a b` there. Then in Monad have `return = pure`. So this will fix *that* problem. – Will Ness Sep 26 '18 at 09:16
  • Why not just combine `StateT` with `Either`, or `ExceptT` with `State`? – Mark Seemann Sep 26 '18 at 09:35
  • @MarkSeemann Because I am a Haskell newbie, trying to create his first monad, and don't want to mess with monad transformers yet. Also, I am not sure if the monad transformer version is more simple to write or more efficient in terms of performance. – mljrg Sep 26 '18 at 09:39
  • That's a perfectly fine reason :) It may be useful to know that those monad transformers exist, though, for later. – Mark Seemann Sep 26 '18 at 09:53
  • 1
    @MarkSeemann Yes, Mark, I know they exist, and also "comonads", "free monads", and all sorts of "costrangenads", but one thing at a time ;-) – mljrg Sep 26 '18 at 09:57

1 Answers1

4

Your error is fixed by changing the code to

  m >>= f = StateError $
    \s -> case runStateError m s of
            (Ok x, s1) -> runStateError (f x) s1
            (Error e, s1) -> (Error e, s1)
        -- or:
        --  (Error e, s1) -> (Error e, s)     -- also works
        -- not:
        --  e             -> e                -- this doesn't

and adding the obvious Functor and Applicative instances,

instance Functor .... where
  fmap = liftM

instance Applicative .... where
  (<*>) = ap
  pure = return

Error e :: Result a e is polymorphic,

data Result a e = Ok a | Error e

so has different types on the left and on the right of that arrow. As it complaints in the error message,

  Expected type: (Result b e, s)
  Actual type: (Result a e, s)

when you use a variable, that makes it be the same on the both sides of the arrow. So e reuses the same value, but Error e creates new value of the appropriate type as needed. And we do need the new type, as demanded by the signature of (>>=):

Monad m => m a -> (a -> m b) -> m b
--          ^^                    ^^
Will Ness
  • 62,652
  • 8
  • 86
  • 167
  • Ok, you helped a lot. See my updated code in the question. Now can you help me in using the `fmap` definition to simplify my monad bind operator implementation? It seems useless being forced to define a functor, and to not leverage it to simplify the monad implementation. The two codes seem very similar, so I guess there is something to be done yet. Otherwise, what's the need for the functor? – mljrg Sep 26 '18 at 10:00
  • without looking, you really should ask *new* question if you have substantial change in the question. I *really* propose you unroll your edit there, and *ask a new question*. thanks. – Will Ness Sep 26 '18 at 10:01
  • Oh, I thought about that, but decided to do it in this question for everything to be at one place. Should still I move this part into another new question, and refer this question in the new one? – mljrg Sep 26 '18 at 10:02
  • 1
    yes, you should roll back your edit, ask new "followup" question and include link to this one as a background. I'll roll it back for you now. :) – Will Ness Sep 26 '18 at 10:03
  • Done, you can see the new question [here](https://stackoverflow.com/questions/52515317/how-to-base-the-definition-of-my-custom-monad-on-its-functor-definition-in-haske). – mljrg Sep 26 '18 at 10:23
  • Forget the question ... I just saw your "adding" link now, with the complete solution to the monad, and this as much better code reuse. Know I just have to understand it :-) Thanks a lot! – mljrg Sep 26 '18 at 10:31
  • yeah, that's a standard definition. see [this](https://stackoverflow.com/questions/11234632/monads-with-join-instead-of-bind/11249714#11249714) (and also, [this](https://stackoverflow.com/a/44118041/849891) and its links). – Will Ness Sep 26 '18 at 14:22