我正在尝试使用Haskell的交互模式通过串口向Lego NXT发送消息,但我无法弄清楚如何正确使用serialport函数。
我有一条消息应该在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
答案 0 :(得分:7)
您需要序列您的Monad
计算。我会写一般的,然后专门针对你的情况。
你遇到的问题是你有一个函数f :: A -> IO B
和另一个函数g :: B -> IO C
,它们觉得它们应该是可以组合的,但不是很完美---第二个函数需要一个普通 B
,而不是第一个返回的IO B
。
这正是Monad
s的力量发挥作用的地方。知道IO
是monad,我们可以使用像(>=>) :: 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
,我们需要从mybrick
IO
“展开”Monad
。所以我们可以使用do
符号
do sp <- mybrick
send sp message
或者,为了使所有内容更整洁,我们可以使用do
表示法运行整个计算
do mybrick <- openSerial "/dev/tty.NXT-DevB" defaultSerialSettings
send mybrick message
答案 1 :(得分: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
绑定只会为等号后面的内容创建一个新名称。在这种情况下,这会导致mybrick
类型为IO SerialPort
,就像错误消息所示。