Создание моей собственной монады State

Я понимаю, как использовать монады, но не совсем понимаю, как их создавать. Итак, я нахожусь в пути, чтобы воссоздать монаду State.

Пока что я создал новый тип Toto (foo по-французски) и сделал его экземпляром Monad. Теперь я пытаюсь добавить к нему «функцию чтения». Я создал класс TotoReader, который объявляет функцию «получить». Но когда я пытаюсь его создать, все разваливается. GHC говорит мне, что не может вывести (m ~ r) (полная ошибка компиляции внизу).

Но когда я создаю функцию get верхнего уровня, все работает правильно.

Итак, как я могу определить функцию get в классе и действительно ли это правильный способ сделать это? Что это я не понимаю?

Мой код пока ниже

{-# 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"

Ошибка компиляции

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'

person Erèbe    schedule 30.03.2014    source источник
comment
Хотя это совершенно правильно, я нашел объявление экземпляра Monad запутанным, поскольку вы использовали Toto a в качестве типа, где a — это переменная типа, представляющая состояние, но затем в реализации вы привязываете a к монаде value, а x и c в гос. Было бы меньше путаницы, если бы вы использовали s для переменной типа и s и s' для привязки состояния.   -  person pat    schedule 31.03.2014


Ответы (1)


Давайте используем ghci для проверки типов:

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

Toto принимает два параметра типа: тип среды и возвращаемый тип. Если r является средой, Toto r будет конструктором монадного типа.

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

TotoReader принимает два параметра типа: конструктор типа монады и тип среды, в нашем случае это Toto r и r соответственно.

Итак, объявление экземпляра должно быть примерно таким:

instance TotoReader (Toto r) r where 
    get = toto $ \x -> (x, x)
person danidiaz    schedule 30.03.2014
comment
Спасибо, я забыл, что если имена разные, типы не могут быть одинаковыми. - person Erèbe; 30.03.2014