I am trying to understand Monads in Haskell and during my countless experiments with code I have encountered this thing:
f2 = do
return \"da\"
First let's just point out that
do
return a
Is exactly the same as
return a
Now, the problem is that return
has the type
return :: Monad m => a -> m a
And when you have a declaration like
foo = bar
where foo
has no arguments haskell makes it "monomorphic". The result of this is that Haskell can't guess what m
is and won't generalize it so you need an explicit type signature. The most general one is
f2 :: Monad m => m String
f2 = return "das"
But you could also use IO
or any other monad
f2 :: IO String
Finally, in your last example, since you're returning 2
, you'd have to give a type signature that indicates you're returning some sort of number, like
f2 :: IO Integer
This is known Monomorphism_restriction
Use signatures
f2 :: Monad m => m String
f2 = do
return "da"
or use language extension:
{-# LANGUAGE NoMonomorphismRestriction #-}
f2 = do
return "da"
to get valid code
When learning about monads it's helpful to expand them out manually yourself, for instance the simple example:
test0 :: IO String
test0 = do
a <- getLine
putStrLn a
return a
If we enable the language extension {-# LANGUAGE ScopedTypeVariables #-}
then we can annotate each of the lines in the monad with it's explicit type which will show the type of the return block.
{-# LANGUAGE ScopedTypeVariables #-}
test1 :: IO String
test1 = do
a <- getLine :: IO String
putStrLn a :: IO ()
return a :: IO String
We can also annotate the explicit type of the left hand side pattern matching which "extracts" from the monad context on the right hand side.
test2 :: IO String
test2 = do
(a :: String) <- getLine :: IO String
(() :: ()) <- putStrLn a :: IO ()
return a :: IO String
We can even expand out the do-notation into its constituting parts:
test3 :: IO String
test3 = getLine >>=
(\a -> putStrLn a >>=
\() -> return a)
Hope that helps build your monad intuition.