2

我正在尝试使用 Haskell 的交互模式通过串行端口向 Lego NXT 发送消息,但我无法弄清楚如何正确使用串行端口功能

我有一条消息应该在 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
4

2 回答 2

7

您需要对计算进行排序Monad。我会针对您的情况进行一般性和简单性的编写。


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

这正是Monads 的力量发挥作用的地方。知道那IO是一个 monad,我们可以使用类似的函数(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c来组合这些Monadic 函数。事实上,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,我们需要mybrickIO Monad. 所以我们可以使用do符号

do sp <- mybrick
   send sp message

do或者,为了让一切更整洁,我们可以使用符号来运行整个计算

do mybrick <- openSerial "/dev/tty.NXT-DevB" defaultSerialSettings
   send mybrick message
于 2013-04-26T23:11:29.093 回答
2

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

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

不同之处在于,let绑定只是为等号之后的任何内容创建一个新名称。在这种情况下,这会导致mybricktype IO SerialPort,就像错误消息所说的那样。

于 2013-04-26T23:13:04.287 回答