Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use read on a string that is not double quoted?

I'm reading values from in from a console using readLn.

I'd like to write a function:

requestValue :: String -> IO a  
requestValue s = do  
  putStrLn $ "Please enter a new value for " ++ s   
  readLn

I'd then be able to do, for example,

changeAge :: Person -> IO Person
changeAge p = do
    age' <- requestValue "age"
    return $ p { age = age'}

changeName :: Person -> IO Person
changeName p = do
    name' <- requestValue "name"
    return $ p { name = name'}

The problem I have is that the read instance of String seems to require the string to be in quotes. I don't want to have to enter "Fred" in the console to change name when I really only want to type in Fred.

Is there an easy way to do this that keeps requestValue polymorphic?

like image 509
Squidly Avatar asked May 14 '11 20:05

Squidly


2 Answers

Since you want to add your own custom read behavior for user names, the way to do that is to actually write a new instance for readings names. To do that we can create a new type for names:

import Control.Arrow (first)

newtype Name = Name { unName :: String }
    deriving (Eq, Ord, Show)

and write a custom read for it:

instance Read Name where
    readsPrec n = map (first Name) . readsPrec n . quote
        where quote s = '"' : s ++ ['"'] 

this is the same as the read instance for strings, but we first quote the string, after reading it in.

Now you can modify your Person type to use Name instead of String:

data Person = Person { age :: Int
                     , name :: Name } deriving Show  

and we're in business:

*Main> changeName (Person 31 (Name "dons"))
Please enter a new value for name
Don
Person {age = 31, name = Name {unName = "Don"}}
like image 181
Don Stewart Avatar answered Sep 27 '22 18:09

Don Stewart


You want getLine, not readLn.

like image 34
augustss Avatar answered Sep 27 '22 17:09

augustss