Самый простой способ — использовать fmap
, который имеет следующий тип:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO
реализует Functor
, а это значит, что мы можем специализировать вышеупомянутый тип, заменив IO
на f
, чтобы получить:
fmap :: (a -> b) -> IO a -> IO b
Другими словами, мы берем некоторую функцию, которая преобразует a
s в b
s, и используем ее для изменения результата действия 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
Оказывается, каждая монада обладает следующим свойством:
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
, за исключением специализированных монад, которые не так универсальны, как функторы.
Итак, если вы хотите преобразовать результат действия IO
, вы можете использовать:
fmap
,
liftM
, or
do
обозначение
Это действительно зависит от вас, какой из них вы предпочитаете. Я лично рекомендую fmap
.
person
Gabriel Gonzalez
schedule
17.02.2013