我正在使用readLn
从控制台读取值。
我想写一个函数:
requestValue :: String -> IO a
requestValue s = do
putStrLn $ "Please enter a new value for " ++ s
readLn
然后我就可以做了,例如,
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'}
我遇到的问题是String的读取实例似乎要求字符串在引号中。当我真的只想输入"Fred"
时,我不想在控制台中输入Fred
来更改名称。
是否有一种简单的方法可以保持requestValue
多态?
答案 0 :(得分:4)
由于您希望为用户名添加自己的自定义read
行为,因此实现此方法的方法是为读数名称实际编写新实例。要做 ,我们可以为名称创建一个新类型:
import Control.Arrow (first)
newtype Name = Name { unName :: String }
deriving (Eq, Ord, Show)
并为其编写自定义read
:
instance Read Name where
readsPrec n = map (first Name) . readsPrec n . quote
where quote s = '"' : s ++ ['"']
这与字符串的读取实例相同,但我们首先引用读取后的字符串。
现在,您可以修改Person
类型以使用Name
代替String
:
data Person = Person { age :: Int
, name :: Name } deriving Show
我们在做生意:
*Main> changeName (Person 31 (Name "dons"))
Please enter a new value for name
Don
Person {age = 31, name = Name {unName = "Don"}}
答案 1 :(得分:2)
您需要getLine
,而不是readLn
。