Question

I understand how to use monads but I don't really grasp how to create one. So I am in the journey to recreate a State monad.

So far I have created a new type Toto (foo in french) and made it an instance of Monad. Now I am trying to add a "reader feature" to it. I created a class TotoReader which declare a "get" function. But when I try to instantiate it, everything fall apart. GHC is telling me that it could not deduce (m ~ r) (full compilation error at the bottom).

But when I create a top level function get, everything is working correctly.

So how can I can define a get function in a class and is it really the right way of doing it ? What is that I don't understand ?

My code so far below

{-# OPTIONS -XMultiParamTypeClasses #-}
{-# OPTIONS -XFlexibleInstances #-}

newtype Toto s val = Toto { runToto :: s -> (val, s) }

toto :: (a -> (b,a)) -> Toto a b
toto = Toto

class (Monad m) => TotoReader m r where
    get :: m r

instance Monad (Toto a) where
    return a = toto $ \x -> (a,x)
    p >>= v  = toto $ \x ->
                    let (val,c) = runToto p x
                    in runToto (v val) c

instance TotoReader (Toto m) r where 
    get = toto $ \x -> (x, x) -- Error here

-- This is working
-- get :: Toto a b
-- get = toto $ \s -> (s,s)


pp :: Toto String String
pp = do 
    val <- get
    return $ "Bonjour de " ++ val

main :: IO ()
main = print $ runToto pp "France"

Compilation error

test.hs:19:11:
    Could not deduce (m ~ r)
    from the context (Monad (Toto m))
      bound by the instance declaration at test.hs:18:10-30
      `m' is a rigid type variable bound by
          the instance declaration at test.hs:18:10
      `r' is a rigid type variable bound by
          the instance declaration at test.hs:18:10
    Expected type: Toto m r
      Actual type: Toto m m
    In the expression: toto $ \ x -> (x, x)
    In an equation for `get': get = toto $ \ x -> (x, x)
    In the instance declaration for `TotoReader (Toto m) r'
Was it helpful?

Solution

Let's use ghci to inspect the kinds:

*Main> :k Toto
Toto :: * -> * -> *

Toto takes two type parameters: the environment type and the return type. If r is the environment, Toto r will be the monad type constructor.

*Main> :k TotoReader
TotoReader :: (* -> *) -> * -> Constraint

TotoReader takes two type parameters: the monad type constructor and the environment type, which in our case are Toto r and r respectively.

So, the instance declaration should be something like:

instance TotoReader (Toto r) r where 
    get = toto $ \x -> (x, x)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top