我了解如何使用单子(monad),但我并不真正掌握如何创建单子(monad)。所以我正在重新创建一个 State monad。
到目前为止,我已经创建了一个新类型 Toto(法语为 foo),并将其作为 Monad 的实例。现在我正在尝试为其添加“阅读器功能”。我创建了一个 TotoReader 类,它声明了一个“get”函数。但当我尝试实例化它时,一切都崩溃了。 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'
最佳答案
让我们使用 ghci 来检查种类:
*Main> :k Toto
Toto :: * -> * -> *
Toto
采用两个类型参数:环境类型和返回类型。如果r
是环境,Toto r
将是monad类型构造函数。
*Main> :k TotoReader
TotoReader :: (* -> *) -> * -> Constraint
TotoReader
采用两个类型参数:monad 类型构造函数和环境类型,在我们的例子中分别是 Toto r
和 r
。
因此,实例声明应该类似于:
instance TotoReader (Toto r) r where
get = toto $ \x -> (x, x)
关于haskell - 创建我自己的状态单子(monad),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22742658/