I have the following function:
sendq :: Socket -> B.ByteString -> String -> IO PortNumber -> IO ()
sendq s datastring host port = do
hostAddr <- inet_addr host
sendAllTo s datastring (SockAddrInet port hostAddr)
whereas sendAllTo has the function signature
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
The problem is the from previous functions I get handed down an IO PortNumber where SockAddr takes a PortNumber only. I tried to make these two compatible by promoting sendAllTo into the IO monad:
liftM sendAllTo s datastring (SockAddrInet port hostAddr)
but no joy. Tells me something about to many arguments. Is this a case for liftM? How do I apply it correctly?
liftMand friends serve the purpose of jacking up pure functions to a monadic setting, much as theApplicativecombinators do.There are two issues with the code you tried. One is just a lack of parentheses.
which is not what you meant. The other issue is that
is a monadic operation, so lifting it will deliver two layers of
IO. The usual method is to parenthesize the pure prefix of the application, like soYou can then build the argument with
liftM2.That gives you
which will achieve precisely nothing as it stands, because it explains how to compute an operation but doesn’t actually invoke it! That’s where you need
or, more compactly
Plug. The Strathclyde Haskell Enhancement supports idiom brackets, where
(|f a1 .. an|) :: m tiff :: s1 -> ... -> sn -> tanda1 :: m s1…an :: m sn.These do the same job for
Applicative mas theliftMfamily do for monads, treatingfas a pure n-ary function anda1..anas effectful arguments.Monads can and should beApplicativetoo, soand
The notation then allows you to invoke computed monadic computations like the above, with a postfixed
@.Note that I’m still parenthesizing the pure prefix of the application, so that the
fof the template is the whole of(sendAllTo s datastring). The notation allows you to mark pure arguments in any position with a~, so you can write thisif the mood takes you.
Rant. We spend far too much energy on figuring out the right
liftM,join,=<<,do,(|...~...@|)punctuation in order to explain how to cut up a type as a value-explaining kernel (()here) in an effect-explaining context (IOhere). If this up-cutting were made more explicitly in types, we should need less noise in our programs to slap values and computations into alignment. I should much prefer the computer to infer where the~and@marks go, but as things stand, Haskell types are too ambiguous a document to make that feasible.