我试图通过串行端口发送消息到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
,就像错误消息所说的那样。