Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell IO example

Tags:

io

haskell

monads

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?

like image 217
Ryan Cori Avatar asked Dec 21 '22 08:12

Ryan Cori


2 Answers

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:

  1. 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.

  2. 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 #-}
    
like image 123
hugomg Avatar answered Dec 26 '22 00:12

hugomg


I've done two changes to the code.

  1. Fix the indentation -- remember that haskell is "space sensitive", so please make sure the code looks properly aligned
  2. Explicit type signatures. This is a bit tricky. But as rule of thumb, when code that you would've expect to work doesn't. Try to annotate your code with types, like shown below. You'll understand why this is so in time.

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
like image 25
Tarrasch Avatar answered Dec 25 '22 22:12

Tarrasch