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?
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"}}
You want getLine
, not readLn
.
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