如何堆叠 State 和 IO 单子

4
在一个Connect4游戏中:
  • 我们从一个空的网格开始
  • 两个玩家在网格上放置棋子x和o
  • 第一个达到4个相连棋子的玩家获胜!
  • 这是一个基于文本的控制台游戏
我的想法是:
  • 在游戏的每一步中,网格都会从一种状态转变为另一种状态
  • 因此我需要使用State模式
  • 由于这是一个涉及IO的控制台应用程序
  • 在这种情况下,我还需要使用IO模式
这种思路正确吗?
假设上述内容正确,下面哪个选项是正确的?
  • type StateInIO[S,A] = IO[State[S,A]]
  • type IOInState[S,A] = State[S,IO[A]]
我更喜欢第二个选项,对我来说更有意义。
我可以以这种方式堆叠这些模式(State, IO)吗?
2个回答

5
在cats中,State实际上被定义为单子变换器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)]],我觉得它是更有用的堆栈,因为现在你可以轻松地使用这个堆栈将代码与应用程序的其余部分连接起来。
您可以在cats的State文档中的交错效果部分中找到有关使用StateT堆叠状态单子和效果单子的更多信息。

1
对于 F = IOF[S => F[(S, A)]] 的结果是 IO[S => IO[(S, A)]],而 IO[State[S, A]] 实际上是 IO[S => (S, A)],因此中间段落似乎不太正确。 - Andrey Tyukin
@AndreyTyukin 好的,我改了。谢谢。 - Krzysztof Atłasik
另一方面,文档指出:“此数据类型表示形式为S => F[(S, A)]的计算。”,但外部的F被省略了...我不确定为什么。 - Andrey Tyukin

1
我认为你应该先编写一个工作程序,然后在通用化/重构之后进行改进,而不是事先过度设计。您需要 State[S, A] 用于业务逻辑和 IO[A] 用于与控制台交互。但是您不必混合使用它们。
然后,在您编写用户交互循环的主类中,您可以利用 StateT 来统一这两种类型。
顺便说一下,IO[State[S,A]] 看起来不像是非常有用的类型。它表示您可以从真实世界中读取 State[S, A]State 是一个函数。没有合理的方法从现实世界中读取函数。因此,最可能您需要一些功能更少的东西。当然,在单子变形器的上下文中,这种类型是有意义的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接