【问题标题】:How to stack the State and IO monads如何堆叠 State 和 IO monad
【发布时间】:2019-10-29 21:33:58
【问题描述】:

在 Connect4 游戏中:

  • 我们从一个空网格开始
  • 两名玩家将棋子 x 和 o 放在网格上
  • 第一个达到 4 个连线的玩家获胜!
  • 这是一款基于文本的主机游戏

我的想法是:

  • 在游戏的每一步,网格都会从一种状态转换到另一种状态
  • 因此我需要使用 State monad
  • 因为这是一个基于控制台的应用程序,涉及到 io
  • 在这种情况下,我还需要使用 IO monad

这种想法正确吗?

假设以上是正确的,其中哪一项是正确的?

  • 类型 StateInIO[S,A] = IO[State[S,A]]
  • 类型 IOInState[S,A] = 状态[S,IO[A]]

我赞成第二种选择,对我来说更有意义。

我可以用这种方式堆叠这些 monad (State, IO) 吗?

【问题讨论】:

    标签: scala functional-programming monads scala-cats


    【解决方案1】:

    我认为您应该只编写一个工作程序,然后对其进行概括/重构,而不是预先进行过度设计。 您需要State[S, A] 用于业务逻辑,IO[A] 用于与控制台交互。但你不必混合它。

    然后在您编写用户交互循环的主类中,您可以利用 StateT 来统一您的两种类型。

    顺便说一句,IO[State[S,A]] 看起来不是一个非常有用的类型。它说您可以从现实世界中读取State[S, A]State 是一个函数。没有明智的方法可以从现实世界中读取函数。所以很可能你需要一些不那么强大的东西。当然,这种类型在 monad 转换器的上下文中是有意义的。

    【讨论】:

      【解决方案2】:

      cats 中的State 实际上定义为monad 转换器StateT[F[_], S, A] 的类型别名,其中F[_] 是效果类型。这个别名用Eval 修复了F[_],所以它看起来像type State[S, A] = StateT[Eval, S, A]。在您的情况下,您应该只定义自己的 IO 状态,例如 type IOState[S, A] = StateT[IO, S, A]

      StateT[IO, S, A] 对应于 IO[S => IO[(S, A)]],我觉得它是更有用的堆栈,因为您现在可以轻松地将使用此堆栈的代码与使用 IO 的应用程序的其余部分连接起来。

      您可以在来自猫的状态文档中的interleaving effects 部分中找到更多关于使用StateT 堆叠状态单子和效果单子的信息。

      【讨论】:

      • F[S => F[(S, A)]] for F = IOIO[S => IO[(S, A)]],而IO[State[S, A]] 本质上是IO[S => (S, A)],因此中间段落似乎不太对。
      • @AndreyTyukin 对,我改了。谢谢。
      • 另一方面:the documentation 声明 “此数据类型表示 S => F[(S, A)] 形式的计算。”,无论出于何种原因,外部 F 是, 省略了...我不知道为什么。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-30
      • 2011-05-07
      • 1970-01-01
      相关资源
      最近更新 更多