質問

I'm trying to send a message via the serial port to a Lego NXT using Haskell's interactive mode but I cannot figure out how to use the serialport functions correctly.

I have a message that should play a tone on the NXT which is of type ByteString

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

I can open the serial port using openSerial.

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

But then I get stuck. How should I use the send function?

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

This gives my the following error 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
役に立ちましたか?

解決

You need to sequence your Monad computations. I'll write it generally and simply then specifically for your situation.


The problem you have is you have a function f :: A -> IO B and another function g :: B -> IO C which feel like they ought to be combinable, but aren't quite---the second function needs a plain B, not the IO B that the first one returns.

This is exactly where the power of Monads comes into play. Knowing that IO is a monad, we can use a function like (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c to combine these Monadic functions. In fact, already, f >=> g :: A -> IO C like we require.

We can also use do notation which would require us to "bind" the return type of f and then apply that to g to get the output.

\a -> do b <- f a
         g b

which gives us a function of type A -> IO C again. In fact, this do notation is basically the definition of (>=>).


So how does this apply in your particular circumstance? Well,

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

gives you a mybrick :: IO SerialPort value. In order to use send :: SerialPort -> ByteString -> IO Int we need to "unwrap" mybrick from the IO Monad. So we can use do notation

do sp <- mybrick
   send sp message

Or, to make everything neater, we can just run the entire computation using do notation

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

他のヒント

openSerial path settings is an IO action, which produces a serial port. To get access to the serial port, you have to execute that action inside the IO monad. Your main could look something like this:

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

The difference is, that the let binding just creates a new name for whatever comes after the equals sign. In this case, this leads to mybrick having type IO SerialPort, just like the error message says.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top