2013-04-26 108 views
2

我试图使用Haskell的交互模式通过串口发送消息给乐高NXT,但我无法弄清楚如何正确使用serialport函数。与Haskell交互使用串口

我有一个消息,应该在它的类型是ByteString

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

我可以打开使用openSerial串口的NXT播放音。

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 

回答

7

您需要序列Monad计算。我会一般性地写出来,然后专门针对你的情况。


你的问题是,你有一个函数f :: A -> IO B和另一个函数g :: B -> IO C这感觉他们应该是组合,但都不太---第二个功能需要平原B,而不是第一个返回的是IO B

这正是Monad的力量发挥作用的地方。知道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,我们需要从IOMonad“解开”mybrick。因此,我们可以使用do符号

do sp <- mybrick 
    send sp message 

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

do mybrick <- openSerial "/dev/tty.NXT-DevB" defaultSerialSettings 
    send mybrick message 
+1

我终于搞定了!必须打开ghci(:set + m)上的多行输入,但我发现使用'send mybrick $ message'更容易。我也收到了错误消息,在开始时缺少两个消息长度字节,但现在我的NXT发出了一个声音:D – ihatetoregister 2013-04-28 17:53:05

+0

我以前从未使用'+ m',这是一个很好的发现!真高兴你做到了! – 2013-04-28 19:54:12

+0

另外,'f a b c d $ e'与'f a b c d e'完全相同,所以'send mybrick message'应该与'send mybrick $ message'相同。 – 2013-04-28 19:54:58

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,就像错误消息所述。