Какова правильная реализация addToLeftPlayerPos - PullRequest
0 голосов
/ 22 февраля 2019

Я пытаюсь следовать этой статье о функционально-реактивном программировании, но я застрял на втором примере в разделе 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 голосов
/ 22 февраля 2019

Несколько фраз

Когда у вас есть стек трансформаторов, вы должны «поднять» свои операции, чтобы запустить функции внутренней монады.например:

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
...