【问题标题】:A monad of tuples (monad, state)?元组的单子(单子,状态)?
【发布时间】:2015-02-21 15:37:14
【问题描述】:

我正在尝试使用 Monads 编写一个 Haskell“猜数”游戏程序,但我被卡住了:
我尝试了简单的状态单子:

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)

并且我需要在元组(a,S)的“IO端”执行IO任务,也就是说,我尝试做类似的事情:

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 使用了不同的符号,以便区分它们。

【问题讨论】:

  • 代码很混乱。你能解释一下你想要做什么吗?在我看来,您正在尝试构建类似StateT s IO a 的东西。您可以在 Control.Monad.Trans.State.StrictControl.Monad.Trans.State.Lazy 中找到它的变体。但我不能肯定地说,因为我看不懂你的代码。
  • @dfeuer 我想在元组的两个部分中分离 monad 动作和状态记录: (monad, state),这样我可以在需要时调用 fst 和 snd 来分离它们和执行一些合适的动作。我不知道怎么解释清楚,也许我写完代码后可以展示这个monad的用法,然后我这里要构建什么就清楚了。

标签: haskell io monads state-monad


【解决方案1】:

(>>=)的类型是:

Monad m => m a -> (a -> m b) -> m b

由于您正在为SM 编写实例,因此您的实例中的绑定类型为

SM a -> (a -> SM b) -> SM b

注意ab 都是完全不受限制的类型变量。这意味着无论我选择在其中放置什么类型,您提供的任何实现都必须工作。特别是,我可以为ab 选择Int

SM Int -> (Int -> SM Int) -> SM Int

现在很清楚为什么您的实现不好:它会尝试将Int 视为单子动作并调用(>>=)

如果您希望能够在绑定中执行单子操作,则必须以某种方式谈论您类型中的单子;例如,一种标准方法是定义

data SMT m a = SMT (S -> m (a, S))

并给出如下实例:

instance Monad m => Monad (SMT m) where -- ...

如果您愿意,可以使用 Identity monad 作为嵌套的 monad,然后恢复正常的 SM

【讨论】:

  • S -> (m a, S) 不起作用,它无法让内部 monad 影响状态。你需要S -> m (a, S)
  • @ØrjanJohansen 我也不希望内部 monad 影响状态,但仍然感谢您的建议。
  • @ØrjanJohansen 这当然是另一种选择;它更通用。感谢您指出。
  • 我的意思是,除非内部 monad 的位置可以影响状态,否则我看不出如何使组合类型成为 monad。问题在于,使用bind :: (S -> (m a, S)) -> (a -> (S -> (m b, S)) -> (S -> (m b, S)),您没有机会以纯粹获得最终S 的方式线程化状态,因为您只能通过使用a 来获得它,而您只能从@987654343 内部获得它@monad。
  • @ØrjanJohansen 谢谢!我打算评论这样一个问题,并想知道我是否应该以某种方式定义一个函数 m a -> a。但是方法 S -> m (a, S) 将如何解决这个问题呢?我可以从 m (a, S) 中得到 a 吗?
猜你喜欢
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
  • 2019-12-13
  • 2016-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多