什么是addToLeftPlayerPos的正确实现

时间:2019-02-22 13:11:14

标签: haskell monads frp reader writer

我试图遵循this paper进行函数式反应式编程,但是我只停留在4.2节中的第二个示例上。

我得到了第一个示例,其中的阅读器转换器已启动并运行:

module Main where

import FRP.BearRiver    
import Control.Monad.Trans.MSF.Reader

type Game = Ball
type Ball = Int

type GameEnv = ReaderT GameSettings

data GameSettings
    = GameSettings
      { leftPlayerPos  :: Int
      , rightPlayerPos :: Int
      }

ballToRight :: Monad m => MSF (GameEnv m) () Ball
ballToRight =
    count >>> arrM addToLeftPlayerPos
    where
      addToLeftPlayerPos =
          (\n -> (n +) <$> asks leftPlayerPos)

hitRight :: Monad m => MSF (GameEnv m) Ball Bool
hitRight = arrM (\i -> (i >=) <$> asks rightPlayerPos)

但是下一步,我正在努力。我无法弄清楚如何正确引入作家转换器,因为我什至无法编译它:

module Main where

import FRP.BearRiver
import Control.Monad
import Control.Monad.Trans.MSF.Reader
import Control.Monad.Trans.MSF.Writer

type Game = Ball
type Ball = Int

type GameEnv m =
    WriterT [String] (ReaderT GameSettings m)

data GameSettings
    = GameSettings
      { leftPlayerPos  :: Int
      , rightPlayerPos :: Int
      }

ballToRight :: Monad m => MSF (GameEnv m) () Ball
ballToRight =
    count >>> arrM addLeftPlayerPos >>> arrM checkHitR
    where
      addLeftPlayerPos =
          (\n -> (n +) <$> asks leftPlayerPos)
      checkHitR n = do
          rp <- asks rightPlayerPos
when (rp > n) $ tell ["Ball is at " ++ (show n)]

实际上,函数调用addLeftPlayerPos所在的行是有问题的,因为本文没有提供函数arrM addLeftPlayerPos,而我的版本似乎缺少WriterT类型签名,因为类型别名type GameEnv m ...建议

addLeftPlayerPos函数的正确实现是什么?

编辑: 编译器错误是:

 Expected type: MSF (GameEnv m) () Ball
    Actual type: MSF (ReaderT GameSettings m1) () () 
• In the expression: 
    count >>> arrM addToLeftPlayerPos >>> arrM checkHitR 

1 个答案:

答案 0 :(得分:2)

一些措辞

拥有变压器堆栈时,必须“提升”操作以运行内部monad功能。例如:

type MyMonad a = Transformer1 (Transformer2 IO) a

这里的堆栈是IO的Transformer2的Transformer1。 “外部” monad是Transformer1,它将Transformer2与IO的最内部(或底部,底部)monad封装在一起。在您的情况下,堆栈实际上是某个未知monad m的读者的作家,都很好。

现在,如果要在功能f :: Transformer2 IO a中运行g :: MyMonad,则必须lift f。同样,如果我们有getLine :: IO String,并且希望从g :: Transformer1 (Transformer2 IO) a内部运行,那么我们可以lift (lift getLine)

升降

如果导入Control.Monad.Trans.Class,则可以取消ReaderT的操作。例如lift (asks ...)而不是asks ...

确实有帮助。您评论的错误可能是由于在asks中使用了checkHitR

还有一个类型错误

提起我们后出现错误:

frosch.hs:23:5: error:
    • Couldn't match type ‘()’ with ‘Int’
      Expected type: MSF (GameEnv m) () Ball
        Actual type: MSF
                       (WriterT [[Char]] (ReaderT GameSettings m)) () ()

这是因为您的checkHitR不返回值rp(我认为应该返回)。解决该问题后,我们得到了以下最终代码:

module Main where

import FRP.BearRiver
import Control.Monad.Trans.Class
import Control.Monad
import Control.Monad.Trans.MSF.Reader
import Control.Monad.Trans.MSF.Writer

type Game = Ball
type Ball = Int

type GameEnv m =
    WriterT [String] (ReaderT GameSettings m)

data GameSettings
    = GameSettings
      { leftPlayerPos  :: Int
      , rightPlayerPos :: Int
      }

ballToRight :: Monad m => MSF (GameEnv m) () Ball
ballToRight =
    count >>> arrM addLeftPlayerPos >>> arrM checkHitR
    where
      addLeftPlayerPos =
          (\n -> (n +) <$> lift (asks leftPlayerPos))
      checkHitR n = do
          rp <- lift (asks rightPlayerPos)
          when (rp > n) $ tell ["Ball is at " ++ (show n)]
          pure rp