在重新启动内执行MonadIO操作

Ryo*_*Oka 8 haskell frp reactive-banana

在反应香蕉,我试图运行reactimate :: Event (IO ()) -> Moment ()与一些动作ArduinohArduino包,的一个实例MonadIO.Arduino a -> IO a包中似乎没有提供任何功能.你将如何执行Arduino行动reactimate

gel*_*sam 2

您将如何执行 Arduino 操作reactimate

我会通过执行具有可观察到的副作用的 IO 操作来间接执行它们。然后,在内部withArduino,我会观察这个副作用并运行相应的 Arduino 命令。

这是一些示例代码。首先,让我们摆脱进口。

{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables #-}

import Control.Monad.IO.Class
import Data.IORef
import Data.Word
import Reactive.Banana
import Reactive.Banana.Frameworks
import Text.Printf
Run Code Online (Sandbox Code Playgroud)

由于我没有 arduino,因此我必须从 hArduino 模拟一些方法。

newtype Arduino a = Arduino (IO a)
  deriving (Functor, Applicative, Monad, MonadIO)

newtype Pin = Pin Word8

pin :: Word8 -> Pin
pin = Pin

digitalWrite :: Pin -> Bool -> Arduino ()
digitalWrite (Pin n) v = Arduino $ do
    printf "Pretend pin %d on the arduino just got turned %s.\n"
           n (if v then "on" else "off")

digitalRead :: Pin -> Arduino Bool
digitalRead (Pin n) = Arduino $ do
    printf "We need to pretend we read a value from pin %d.\n" n
    putStrLn "Should we return True or False?"
    readLn

withArduino :: Arduino () -> IO ()
withArduino (Arduino body) = do
    putStrLn "Pretend we're initializing the arduino."
    body
Run Code Online (Sandbox Code Playgroud)

在代码的其余部分中,我将假装 Arduino 和 Pin 类型是不透明的。

我们需要一个事件网络来将表示从 arduino 接收到的信号的输入事件转换为描述我们想要发送到 arduino 的内容的输出事件。为了让事情变得非常简单,让我们从一个引脚接收数据并在另一个引脚上输出完全相同的数据。

eventNetwork :: forall t. Event t Bool -> Event t Bool
eventNetwork = id
Run Code Online (Sandbox Code Playgroud)

接下来,让我们将事件网络连接到外部世界。当输出事件发生时,我只需将值写入 IORef,稍后我就可以观察到。

main :: IO ()
main = do
    (inputPinAddHandler, fireInputPin) <- newAddHandler
    outputRef <- newIORef False

    let networkDescription :: forall t. Frameworks t => Moment t ()
        networkDescription = do
            -- input
            inputPinE <- fromAddHandler inputPinAddHandler

            -- output
            let outputPinE = eventNetwork inputPinE

            reactimate $ writeIORef outputRef <$> outputPinE
    network <- compile networkDescription
    actuate network

    withArduino $ do
      let inputPin  = pin 1
      let outputPin = pin 2

      -- initialize pins here...

      -- main loop
      loop inputPin outputPin fireInputPin outputRef
Run Code Online (Sandbox Code Playgroud)

请注意如何在主循环之外调用 和reactimatecompile这些函数设置您的事件网络,您不想在每个循环中调用它们。

最后,我们运行主循环。

loop :: Pin
     -> Pin
     -> (Bool -> IO ())
     -> IORef Bool
     -> Arduino ()
loop inputPin outputPin fireInputPin outputRef = do
    -- read the input from the arduino
    inputValue <- digitalRead inputPin

    -- send the input to the event network
    liftIO $ fireInputPin inputValue

    -- read the output from the event network
    outputValue <- liftIO $ readIORef outputRef

    -- send the output to the arduino
    digitalWrite outputPin outputValue

    loop inputPin outputPin fireInputPin outputRef
Run Code Online (Sandbox Code Playgroud)

请注意我们如何liftIO在 Arduino 计算内部与事件网络进行交互。我们调用fireInputPin来触发输入事件,事件网络导致输出事件被触发作为响应,并且writeIORef我们给出的 toreactimate导致输出事件的值被写入 IORef。如果事件网络更复杂并且输入事件没有触发任何输出事件,则IORef的内容将保持不变。无论如何,我们可以观察该内容,并使用它来确定要运行哪个 Arduino 计算。在这种情况下,我们只需将输出值发送到预定的引脚即可。