Haskell 创建我自己的状态单子

Haskell 创建我自己的状态单子,haskell,monads,state-monad,Haskell,Monads,State Monad,我知道如何使用单子,但我并不真正掌握如何创建单子。因此,我正在重建一个状态单子的过程中 到目前为止,我已经用法语创建了一个新类型totofoo,并将其作为Monad的一个实例。现在我正在尝试向它添加一个阅读器功能。我创建了一个TotoReader类,它声明了一个get函数。但当我尝试实例化它时,一切都崩溃了。GHC告诉我,它无法从底部推断m~r完整编译错误 但是当我创建一个顶级函数get时,一切都正常工作 那么,如何在类中定义get函数呢?这真的是正确的方法吗?我不明白的是什么 我的代码如下 {

我知道如何使用单子,但我并不真正掌握如何创建单子。因此,我正在重建一个状态单子的过程中

到目前为止,我已经用法语创建了一个新类型totofoo,并将其作为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"
让我们使用ghci检查以下种类:

*Main> :k Toto
Toto :: * -> * -> *
Toto接受两个类型参数:环境类型和返回类型。如果r是环境,那么Toto r将是monad类型的构造函数

*Main> :k TotoReader
TotoReader :: (* -> *) -> * -> Constraint
TotoReader接受两个类型参数:monad类型构造函数和环境类型,在本例中分别是ToToR和r

因此,实例声明应该类似于:

instance TotoReader (Toto r) r where 
    get = toto $ \x -> (x, x)
让我们使用ghci检查以下种类:

*Main> :k Toto
Toto :: * -> * -> *
Toto接受两个类型参数:环境类型和返回类型。如果r是环境,那么Toto r将是monad类型的构造函数

*Main> :k TotoReader
TotoReader :: (* -> *) -> * -> Constraint
TotoReader接受两个类型参数:monad类型构造函数和环境类型,在本例中分别是ToToR和r

因此,实例声明应该类似于:

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

谢谢,我忘了如果名称不同,类型就不可能相同。谢谢,我忘了如果名称不同,类型就不可能相同。虽然完全正确,但我发现Monad实例声明令人困惑,因为您使用Toto a作为类型,其中a是表示状态的类型变量,但在实现中,将a绑定到monad值,将x和c绑定到state。如果将s用于类型变量,将s和s'用于状态绑定,那么就不会那么混乱了。虽然完全正确,但我发现Monad实例声明令人困惑,因为您使用Toto a作为类型,其中a是表示状态的类型变量,但在实现中,您将a绑定到Monad值,x和c表示状态。如果将s用于类型变量,将s和s'用于状态绑定,那么就不会那么混乱了。