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.
1 Answer 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 anEmptyMonad a, and a functiona -> EmptyMonad band returns anEmptyMonad belement.
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.
2 Comments
EmptyMonad fx (y+1) instead of fx {myState = y+1}?fx is an EmptyMonad b, not a b. So EmptyMonad fx (y+1) would be an EmptyMonad (EmptyMonad b) -- a nested monadic value!
instance Monad EmptyMonad where(withouta).(EmptyMonad x y) >>= f = EmptyMonad (f x) (y + 1). (withf), otherwise the types do not match.returnis an identity" law saying thatreturn 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!)