在reactimate内部执行MonadIO操作 [英] Execute MonadIO action inside of reactimate

查看:94
本文介绍了在reactimate内部执行MonadIO操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在反应香蕉中,我试图在Arduino的某些操作来运行reactimate :: Event (IO ()) -> Moment () -Arduino.html> hArduino程序包(MonadIO的实例).软件包中没有提供Arduino a -> IO a的功能.您将如何在reactimate中执行Arduino动作?

In reactive-banana, I am trying to run reactimate :: Event (IO ()) -> Moment () with some actions of Arduino in hArduino package, an instance of MonadIO. There seems no function of Arduino a -> IO a provided in the package. How would you execute Arduino actions in reactimate?

推荐答案

您将如何在reactimate中执行Arduino操作?

How would you execute Arduino actions in reactimate?

我将通过执行具有明显副作用的IO动作来使它们间接执行.然后,在withArduino内部,我将观察到这种副作用并运行相应的Arduino命令.

I would cause them to be executed indirectly, by executing an IO action which has an observable side-effect. Then, inside withArduino, I would observe this side-effect and run the corresponding Arduino command.

这是一些示例代码.首先,让我们避免导入.

Here's some example code. First, let's get the imports out of the way.

{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables #-}

import Control.Monad.IO.Class
import Data.IORef
import Data.Word
import Reactive.Banana
import Reactive.Banana.Frameworks
import Text.Printf

由于我没有arduino,所以我不得不从hArduino中模拟一些方法.

Since I do not have an arduino, I'll have to mock up a few methods from 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

在其余的代码中,我会假装Arduino和Pin类型是不透明的.

In the rest of the code, I'll pretend that the Arduino and Pin types are opaque.

我们需要一个事件网络将代表从arduino接收到的信号的输入事件转换成描述我们要发送到arduino的输出事件.为了使事情变得非常简单,让我们从一个引脚接收数据,然后在另一引脚上输出完全相同的数据.

We'll need an event network to transform input events representing signals received from the arduino into output events describing what we want to send to the arduino. To keep things extremely simple, let's receive data from one pin and output the exact same data on another pin.

eventNetwork :: forall t. Event t Bool -> Event t Bool
eventNetwork = id

接下来,让我们将活动网络连接到外部世界.当发生输出事件时,我只需将值写入IORef中,稍后便会观察到.

Next, let's connect our event network to the external world. When output events occur, I simply write the value into an IORef, which I'll be able to observe later.

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

请注意在主循环之外如何仅调用一次reactimatecompile.这些功能设置了事件网络,您不想在每个循环中都调用它们.

Note how reactimate and compile are only called once, outside the main loop. Those functions setup your event network, you do not want to call them on every loop.

最后,我们运行主循环.

Finally, we run the main loop.

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

请注意我们如何使用liftIO从Arduino计算内部与事件网络进行交互.我们调用fireInputPin来触发输入事件,事件网络导致响应时触发了输出事件,而我们赋予reactimatewriteIORef导致将输出事件的值写入IORef.如果事件网络更加复杂,并且输入事件未触发任何输出事件,则IORef的内容将保持不变.无论如何,我们可以观察该内容,并使用它来确定要运行的Arduino计算.在这种情况下,我们只需将输出值发送到预定的引脚即可.

Note how we use liftIO to interact with the event network from inside an Arduino computation. We call fireInputPin to trigger an input event, the event network causes an output event to be triggered in response, and the writeIORef we gave to reactimate causes the output event's value to be written to the IORef. If the event network was more complicated and the input event did not trigger any output event, the contents of the IORef would remain unchanged. Regardless, we can observe that contents, and use it to determine which Arduino computation to run. In this case, we simply send the output value to a predetermined pin.

这篇关于在reactimate内部执行MonadIO操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆