I understand (somewhat) monads and understand that the operator <- will extract the value from the monad.
But how does it work with different types?
Typically, I have seen it being used to extract strings from IO monad. But in the example code below am not able to see why it fails in main 3rd line, complaining that it is expecting a type of IO int? How is the compiler infering that an IO int is needed?
Also what does it ( <-
) do in the multWithLog
method?
import Control.Monad.Trans.Writer.Lazy
main = do
putStrLn $ show $ logNumber 3 -- prints WriterT (Identity (3,["Got Number: 3"]))
putStrLn $ show $ multWithLog -- prints WriterT (Identity (3,["Got Number: 3"]))
_ <- logNumber 3 -- fails with Couldn't match type ‘WriterT [String] Data.Functor.Identity.Identity’ with ‘IO’
-- Expected type: IO Int
-- Actual type: Writer [String] Int
putStrLn "test"
logNumber :: Int -> Writer [String] Int
logNumber x = writer (x, ["Got Number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
--b <- logNumber 5
return a
Every statement in a do
block must be from the same monadic type.
In
multWithLog = do
a <- logNumber 3
return a
we have logNumber 3 :: Writer [String] Int
and return a :: (Monad m) => m Int
(which is polymorphic), so the whole thing typechecks as Writer [String] Int
(with m = Writer [String]
, which is a monad).
In
main = do
putStrLn $ show $ logNumber 3
putStrLn $ show $ multWithLog
_ <- logNumber 3
putStrLn "test"
we have putStrLn ... :: IO ()
and logNumber 3 :: Writer [String] Int
. This is a type error because Writer [String]
is not the same as IO
.
The underlying reason is that do
blocks are just syntactic sugar for calls to >>=
and >>
. Eg your main
really means
main =
(putStrLn $ show $ logNumber 3) >>
(putStrLn $ show $ multWithLog) >>
logNumber 3 >>= \_ ->
putStrLn "test"
with
(>>) :: (Monad m) => m a -> m b -> m b
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
which requires the type m
to remain the same throughout.
Be careful with wording such as
extract the value from the monad
A monad doesn't contain 'a' value. For example, Maybe
contains zero or one value. List ( []
) contains multiple values. See this answer for more details.
In the list case, for example, the <-
operator extracts each of the list values, one at a time.
When you use do
notation, all values extracted must belong to the same Monad
. In the OP, the compiler infers that the Monad
in question is IO
, since putStrLn
returns IO ()
values.
logNumber
, on the other hand, returns Writer [String] Int
values, and while that's also a Monad
instance, it's not the same as IO
. Therefore, the code doesn't type check.
Kepping things simple. These are two facts
Writer [String]
is actually monad, so Writer [String] Int
can be seen as m Int
do
block should happen within the same monad. In the main
function the compiler is reasoning as follows:
IO
monad since putStrLn ...
is of type IO ()
_ <- logNumber 3
. Since I am in the IO
monad, logNumber 3
should be IO WhatEver
logNumber 3
is actually a monadic value of type m Int
m
is the Writer [String]
monad, not the IO
monad Writer [String] Int
is incorrect an It should be IO Int
So that is where IO Int
comes from. I am just triying to be pedagogic here. Check @melpomene's answer for a complete explanation
Hope it helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.