我试图遵循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
答案 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