Самый простой способ заключается в использовании 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
или
do
обозначения
Это действительно до вам, который вы предпочитаете. Я лично рекомендую fmap
.
Какие учебники вы прочитали - каждое введение в IO в haskell охватывает ваш вопрос. Решение заключается в создании IO Monad. – epsilonhalbe
Спасибо. Я решил проблему. Посмотрите ниже: –