最简单的方法是使用fmap,其类型如下:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO 实现了Functor,这意味着我们可以通过将IO 替换为f 来特化上述类型得到:
fmap :: (a -> b) -> IO a -> IO b
换句话说,我们采用一些将as 转换为bs 的函数,并使用它来更改IO 操作的结果。例如:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
那里刚刚发生了什么?好吧,map toUpper 有类型:
map toUpper :: String -> String
它将String 作为参数,并返回String 作为结果。具体来说,它将整个字符串大写。
现在,我们来看看fmap (map toUpper)的类型:
fmap (map toUpper) :: IO String -> IO String
我们已升级函数以处理 IO 值。它将IO 操作的结果转换为返回大写字符串。
我们也可以使用do 表示法来实现这一点,以:
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
事实证明,每个 monad 都具有以下属性:
fmap f m = do
x <- m
return (f x)
换句话说,如果任何类型实现了Monad,那么它也应该总是能够实现Functor,使用上面的定义。其实我们可以一直使用liftM作为fmap的默认实现:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM 与 fmap 相同,除了专门用于 monad,它不像函子那样通用。
因此,如果您想转换 IO 操作的结果,您可以使用:
这真的取决于你喜欢哪一个。我个人推荐fmap。