Monad变形金刚 - 随机+错误+登录棋盘游戏服务

时间:2016-02-15 00:25:52

标签: haskell monads monad-transformers

我正在尝试从Go到Haskell(通过电子邮件服务进行棋盘游戏)移植一个规模很大的项目,因为我最近很乐意编写Haskell并且很好地发现它的模型游戏逻辑。

我已经清楚地了解了我之前编写过该服务和25个游戏所要实现的目标,但我不确定这是解决问题的最佳方式,也不确定如何实现它。

大多数暴露的棋盘游戏功能具有以下内容:

  • 随机性
  • 错误(主要是验证,在做违反游戏规则的事情时)
  • 记录(游戏中发生事件时会产生人类可读的日志)

我一直试图使用的单子如下:

我的问题是我正在努力弄清楚如何在一个功能中使用这三个。对于一个相当人为的例子,我为游戏数据定义了一种类型,并将其传递给许多这些函数并从中返回:

data Game = Game { rnd :: Int
                 , numPlayers :: Int
                 , deck :: [Int]
                 , hands :: [[Int]]
                 }

-- newGame takes a player count and returns a Game.  Needs randomness, errors and logging.
newGame :: Int -> ? Game

-- drawCard takes a player number and an existing Game and returns a Game.  Needs errors and logging.
drawCard :: Int -> Game -> ? Game

如果只返回Game或使用单个monad,这些函数的实际实现不是问题。我开始需要组合monad的那一刻就是我开始绊倒自己。

如果有人能够举例说明如何将monad组合成两个人为的功能,我将非常感激。我想,一旦我有几个例子,我可以跳过这个障碍并继续学习。

1 个答案:

答案 0 :(得分:1)

你可以使用monad trasformer来组成一个monad,这将提供一种生成随机数,保持状态和你想要做的其他事情的方法。

让我们从MonadRandom开始吧。文档说IO是它的一个实例,所以我们可以将它用作基础monad:

newtype Game a = Game (IO a)

要使Game存储和修改数据,请将其MonadState包装成StateT

data GameState = GameState {
                 , numPlayers :: Int
                 , deck :: [Int]
                 , hands :: [[Int]]
                 }

newtype Game a = Game (StateT GameState (IO a))

以同样的方式,您可以通过Game教授WriterT提供日志记录工具:

type LogType = String

newtype Game a = Game (WriterT LogType (StateT GameState (IO a)))