I was reading up on the deeper workings of the IO monad in Haskell on their wiki and I came across this code
main = do a <- ask "What is your name?"
b <- ask "How old are you?"
return ()
ask s = do putStr s
readLn
Which makes sense to me. The ask function should print out the string given to it and return a line that can be passed into a or b.
However loading this into GHCi I get problems. Tells me there isn't an instance of Read from use of ask and that I could import GHC.Read. That shouldn't be necessary. This code was on Haskell.org so I would think it should work. Was something in the language changed or is there some big piece of understanding that I am missing?
If you create a file with just the ask function (without the problematic main) and load it to ghci you will be able to see that ask's type is
ask :: (Read a) => String -> IO a
meaning that it is polymorphic in the return type.
The problem is that when you do
a <- ask "What is your name"
the compiler needs to know what is the type of a
so it can use the correct deserializing function for the line you read from the inpout. But a
isn't used anywhere else and there are no type signatures anywere either so the type inference can't deduce the type of a
. The compiler gives up and gives you that "ambiguos types" message.
There are two main ways to fix that:
Make the ask function always return the same type. You can do this either by adding an specific signature
ask :: String -> IO String
Or by changing the readLn
into something like getLine
.
Add type signatures where you are using the polymorphic functions. You can either add a type signature to the ask call itself:
a <- ask "What is your name" :: IO String
or you can add it directly to the variable
(a :: String) <- ask "What is your name"
however, this second option isn't allowed by the default Haskell syntax. you need to enable the ScopedTypeVariables extention by adding the following comment as the first line in your file
{-# LANGUAGE ScopedTypeVariables #-}
I've done two changes to the code.
Here's the modified code:
main = do
a <- askString "What is your name?"
b <- askOther "How old are you?"
putStrLn ""
putStrLn "Name and age"
putStrLn (a :: String)
print (b :: Int)
return ()
askString s = do
putStrLn s
getLine
askOther s = do
putStrLn s
readLn
Edit: Sorry, the code actually compiles now. Again, as your haskell matures, you'll see why askString and askOther does look different. Here is an example run:
$ runghc Hello.hs
What is your name?
Arash
How old are you?
22
Name and age
Arash
22
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With