Reputation: 6515
This is several questions rolled into one:
In do
notation, does each line have to return the same type? For example, can I write one line in a single do
block that returns an IO
monad, and another that returns an integer? (My understanding, based on how the de-sugaring with >>
and >>=
seems to work, is that the answer is no.)
If not, then how does the compiler determine what type the lines must all return? In all the examples I've seen, the author takes it as a foregone conclusion that we're just working with IO
monads. But how do you know, for a given do
block, what each line must return?
Again assuming the answer to #1 is no: How do you use functions that don't return the right kind of monad inside a do
block? For example, consider this websockets code:
application :: MVar ServerState -> WS.Request -> WS.WebSockets WS.Hybi00 ()
application state rq = do
WS.acceptRequest rq
msg <- WS.receiveData :: WS.WebSockets WS.Hybi00 Text
return ()
Suppose I want to print the value of msg
. How would I go about that in a way that doesn't conflict with the type of the do
block?
Upvotes: 4
Views: 715
Reputation: 139870
To answer the final part of your final question,
Suppose I want to print the value of
msg
. How would I go about that in a way that doesn't conflict with the type of thedo
block?
As jozefg said in his answer, monad transformers is usually what you need for this. However, in this case the WebSockets p
monad is not a transformer. But, it is an instance of MonadIO
which is a class for monad stacks which have IO
"at the bottom" and which therefore let you run arbitrary IO
actions from within them.
The MonadIO
class provides the function liftIO
which has the type
liftIO :: MonadIO m => IO a -> m a
In your case, this becomes IO a -> WebSockets Hybi00 a
, so you can use it to convert the print msg
action from IO ()
to WebSockets Hybi00 ()
, which you can then use in the do
block:
application :: MVar ServerState -> WS.Request -> WS.WebSockets WS.Hybi00 ()
application state rq = do
WS.acceptRequest rq
msg <- WS.receiveData :: WS.WebSockets WS.Hybi00 Text
liftIO $ print msg
Upvotes: 4
Reputation: 53871
IO String
and one can return an IO Integer
but they both have to be IO
.let
, remember how in GHCi you must use let to declare local variables, you can do the same in a do
block. let someMonad = doSomething
in
lift
which can "lift" another monad into the transformer. Transformers normally end in a T, eg StateT. Pretty much every monad you use has an equivalent transformer.Upvotes: 10