0

I'm trying to create a very simple monad in Haskell. The monad does nothing special but holding a counter as state.

module EmptyMonad
  ( EmptyMonad
  ) where

import Control.Monad

data EmptyMonad a = EmptyMonad
  { myValue :: a
  , myState :: Int
  } deriving (Show)

instance (Eq a) => Eq (EmptyMonad a) where
  EmptyMonad x1 y1 == EmptyMonad x2 y2 = x1 == x2 && y1 == y2


instance Monad (EmptyMonad a) where
  return x = EmptyMonad x 0
  (EmptyMonad x y) >>= f = EmptyMonad x (y + 1)

After spending few hours on Monads, I cannot get my head around the error from the compiler:

EmptyMonad.hs:16:10: error:
    • Expecting one fewer argument to ‘Monad EmptyMonad’
      Expected kind ‘k0 -> Constraint’,
        but ‘Monad EmptyMonad’ has kind ‘Constraint’
    • In the instance declaration for ‘Monad EmptyMonad a’
Failed, modules loaded: none.
user1
  • 601
  • 1
  • 6
  • 12
  • You should write `instance Monad EmptyMonad where` (without `a`). – Willem Van Onsem Oct 03 '17 at 18:02
  • Furthermore it should be `(EmptyMonad x y) >>= f = EmptyMonad (f x) (y + 1)`. (with `f`), otherwise the types do not match. – Willem Van Onsem Oct 03 '17 at 18:02
  • I get this error if I follow your suggestion, no idea. Couldn't match type ‘b’ with ‘EmptyMonad b’ ‘b’ is a rigid type variable bound by the type signature for: (>>=) :: forall a b. EmptyMonad a -> (a -> EmptyMonad b) -> EmptyMonad b at EmptyMonad.hs:18:20 – user1 Oct 03 '17 at 18:05
  • what if you follow the second suggestion as well? – Willem Van Onsem Oct 03 '17 at 18:05
  • I applied both of the suggestions. – user1 Oct 03 '17 at 18:07
  • 1
    I would advise you to read [**Kinds and some type-foo**](http://learnyouahaskell.com/making-our-own-types-and-typeclasses#kinds-and-some-type-foo) and [**Making monads**](http://learnyouahaskell.com/for-a-few-monads-more#making-monads) – Redu Oct 03 '17 at 18:43
  • 5
    As a complete aside to your problems understanding and using Haskell syntax, you are also going to have a semantic problem. Counting binds is not okay in monads; it violates the "`return` is an identity" law saying that `return x >>= f = f x`, since there are fewer binds on the right-hand side of the equation. (It seems to be everybody's first idea for a new monad, though, including mine!) – Daniel Wagner Oct 03 '17 at 19:01
  • 1
    See [this question](https://stackoverflow.com/questions/4765260/seeking-constructive-criticism-on-monad-implementation) which asks about a "counter monad" also. – luqui Oct 03 '17 at 20:25

1 Answers1

1

There are two main problems here:

  • the instance declaration does expect a type of kind * -> *. So for instance [], not [a]; and
  • the bind operator >>= expect an EmptyMonad a, and a function a -> EmptyMonad b and returns an EmptyMonad b element.

So we can fix the problems with the following solution:

instance Monad EmptyMonad where  -- no a after EmptyMonad
  return x = EmptyMonad x 0
  (EmptyMonad x y) >>= f = fx {myState = y+1}
      where fx = f x

So here we specify instance Monad EmptyMonad since EmptyMonad has kind * -> *. Furthermore the bind operator will calculate f x and then alter the myState of that instance with y+1.

That being said, nowadays you need to make EmptyMonad an instance of Applicative and Functor as well.

user1
  • 601
  • 1
  • 6
  • 12
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Why can't I use `EmptyMonad fx (y+1)` instead of `fx {myState = y+1}`? – user1 Oct 03 '17 at 18:38
  • 2
    @user1 Because `fx` is an `EmptyMonad b`, not a `b`. So `EmptyMonad fx (y+1)` would be an `EmptyMonad (EmptyMonad b)` -- a nested monadic value! – Daniel Wagner Oct 03 '17 at 19:24