Несколько фраз
Когда у вас есть стек трансформаторов, вы должны «поднять» свои операции, чтобы запустить функции внутренней монады.например:
type MyMonad a = Transformer1 (Transformer2 IO) a
Стек здесь Transformer1 из Transformer2 IO.«Внешняя» монада - это Transformer1, которая оборачивает Transformer2 самой внутренней (или базовой, нижней) монадой IO.В вашем случае стек - это фактически Writer of Reader какой-то неизвестной монады 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