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 Monad
s 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 Monad
ic 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