我正在尝试写一个Haskell"数字猜测"游戏程序,使用Monads,但我被困住了:
我尝试了简单的状态monad:
data SM a = SMN (S -> (a, S))
instance Monad SM where
SMN c1 >>= fc2 = SMN (\s0 -> let (r, s1) = c1 s0 in
let SMN c2 = fc2 r in
c2 s1)
我需要在" IO-side"上执行IO任务。元组(a,S),也就是说,我尝试过这样的事情:
SMN c1 >>= fc2 = SMN (\s0 -> let (r, s1) = c1 s0 in
let SMN c2 = fc2 r in
let (r1, s2) = c2 s1 in
let r2 = r1 >>= (\_ -> r) in
(r2, s2))
简而言之,我想要定义的绑定运算符与原始状态monad相同,除了我们绑定r1和将参数带到r的常量函数(以便将两个操作链接在一起)。但是ghc告诉我a是一个刚性类型变量......这是什么意思?我不能在一个绑定操作符中使用另一个绑定操作符?
如果是这样,那么有没有办法实现这样的绑定运算符?怎么样?
因为我是Haskell的新手(我想我可能有一个关于函数的符号错误
\_ -> r
),欢迎任何意见和参考,提前谢谢 附:我对数据类型SM和类型构造函数SMN使用了不同的符号,以便区分它们。
答案 0 :(得分:7)
(>>=)
的类型是:
Monad m => m a -> (a -> m b) -> m b
由于您正在为SM
编写实例,因此实例中的绑定类型为
SM a -> (a -> SM b) -> SM b
请注意,a
和b
都是完全不受限制的类型变量。这意味着无论我选择放置什么类型,您提供的必须工作。特别是,我可以为Int
和a
选择b
:
SM Int -> (Int -> SM Int) -> SM Int
现在很清楚为什么你的实现不好:它会尝试将Int
视为monadic动作,并在其上调用(>>=)
。
如果你想在你的绑定中做单一动作,你将不得不以某种方式谈论你的类型中的monad;例如,一种标准方法是定义
data SMT m a = SMT (S -> m (a, S))
并提供如下实例:
instance Monad m => Monad (SMT m) where -- ...
如果您愿意,可以使用Identity
monad作为嵌套monad恢复正常SM
。