使用串口与Haskell交互



我试图通过串行端口发送消息到Lego NXT使用Haskell的交互模式,但我不知道如何正确使用串行端口功能。

我有一条消息,应该在NXT上播放ByteString类型的音调

> let message = pack ([6, 0 ,0, 3, 224, 1, 208, 7]::[Word8])

我可以使用openSerial打开串口。

openSerial :: FilePath -> SerialPortSettings -> IO SerialPort
> let mybrick = openSerial "/dev/tty.NXT-DevB" defaultSerialSettings

但是我被卡住了。如何使用send功能?

send :: SerialPort -> B.ByteString -> IO Int
> send mybrick message

这给了我以下错误信息:

<interactive>:31:6:
    Couldn't match expected type `SerialPort'
                with actual type `IO SerialPort'
    In the first argument of `send', namely `mybrick'
    In the expression: send mybrick message
    In an equation for `it': it = send mybrick message

您需要排序您的Monad计算。我将概括地、简单地写出来,然后针对你的情况专门写出来。


你的问题是你有一个函数f :: A -> IO B和另一个函数g :: B -> IO C,感觉它们应该是可组合的,但不是完全——第二个函数需要一个普通的 B,而不是第一个返回的IO B

这正是Monad s的力量发挥作用的地方。知道IO是一个单子,我们可以使用一个类似(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c的函数来组合这些Monad ic函数。事实上,f >=> g :: A -> IO C已经像我们要求的那样。

我们也可以使用do符号,这需要我们"绑定"f的返回类型,然后将其应用于g以获得输出。

a -> do b <- f a
         g b

给我们一个类型为A -> IO C的函数。实际上,这个do符号基本上就是(>=>)的定义。


那么这在你的特殊情况下如何应用呢?

let mybrick = openSerial "/dev/tty.NXT-DevB" defaultSerialSettings

给出一个mybrick :: IO SerialPort值。为了使用send :: SerialPort -> ByteString -> IO Int,我们需要从IO Monad中"解包裹"mybrick。所以我们可以使用do符号

do sp <- mybrick
   send sp message

或者,为了使一切更简洁,我们可以使用do符号

运行整个计算。
do mybrick <- openSerial "/dev/tty.NXT-DevB" defaultSerialSettings
   send mybrick message

openSerial path settings是一个IO动作,它产生一个串行端口。要访问串行端口,必须在IO单子内执行该操作。您的main可能看起来像这样:

main = do 
    mybrick <- openSerial "/dev/tty.NXT-DevB" defaultSerialSettings
    let message = pack ([0, 3, 224, 1, 208, 7] :: [Word8])
    send mybrick message

不同之处在于,let绑定只是为等号后面的内容创建一个新名称。在本例中,这导致mybrick的类型为IO SerialPort,就像错误消息所说的那样。

最新更新