В чем разница между liftM и mapM в Haskell - PullRequest
16 голосов
/ 02 мая 2011

В чем разница между функциями liftM и mapM?

Ответы [ 4 ]

30 голосов
/ 02 мая 2011

Они на самом деле не связаны.Я постараюсь объяснить, что делает каждый из них.Я предполагаю, что у вас есть общее представление о том, что такое монада.

liftM :: Monad m => (a -> b) -> (m a -> m b) позволяет вам использовать обычную функцию в монаде.Она берет функцию a -> b и превращает ее в функцию m a -> m b, которая делает то же самое, что и исходная функция, но делает это в монаде.Результирующая функция ничего не «делает» с монадой (не может, потому что исходная функция не знала, что она находится в монаде).Например:

main :: IO ()
main = do
    output <- liftM ("Hello, " ++) getLine
    putStrLn output

Функция ("Hello, " ++) :: String -> String добавляет «Hello» к строке.Передача в liftM создает функцию типа IO String -> IO String - теперь у вас есть функция, которая работает в монаде IO.Он не выполняет ввода-вывода, но может принимать действие ввода-вывода в качестве входных данных и создает действие ввода-вывода в качестве выходных данных.Следовательно, я могу передать getLine в качестве входных данных, и он вызовет getLine, добавит «Hello» в начало результата и вернет его как действие ввода-вывода.

mapM :: Monad m => (a -> m b) -> [a] -> m [b] вполнеразные;обратите внимание, что в отличие от liftM, он принимает монадическую функцию.Например, в монаде IO он имеет тип (a -> IO b) -> [a] -> IO [b].Она очень похожа на обычную функцию map, только она применяет монадическое действие к списку и создает список результатов, заключенный в монадическое действие.Например (довольно плохо):

main2 :: IO ()
main2 = do
    output <- mapM (putStrLn . show) [1, 2, 3]
    putStrLn (show output)

Это печатает:

1
2
3
[(),(),()]

То, что он делает, выполняет итерацию по списку, применяя (putStrLn . show) к каждому элементу в списке(с эффектом ввода-вывода распечатывания каждого из чисел), а также преобразование чисел в значение ().Результирующий список состоит из [(), (), ()] - вывод putStrLn.

21 голосов
/ 02 мая 2011

Во-первых, типы отличаются:

liftM :: (Monad m) => (a -> b) -> m a -> m b
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

liftM переводит функцию типа a -> b в монадный аналог. mapM применяет функцию, которая выдает монадическое значение для списка значений, получая список результатов, встроенных в монаду.

Примеры:

* * 1010

... обратите внимание, что map и mapM отличаются! Э.Г.

> map (x -> [x+1]) [1,2,3]
[[2],[3],[4]]
> mapM (x -> [x+1]) [1,2,3]
[[2,3,4]]
9 голосов
/ 02 мая 2011

Другие ответы уже объяснили это хорошо, поэтому я просто укажу, что вы обычно увидите fmap, используемый вместо liftM в реальном коде Haskell, так как fmap - просто более общая версия в типе класс Functor. Так как все Monad с хорошим поведением также должны быть экземплярами Functor, они должны быть эквивалентными.

Вы также можете увидеть оператор <$>, используемый в качестве синонима для fmap.

Также, mapM f = sequence . map f, так что вы можете думать об этом как о превращении списка значений в список действий, а затем о выполнении действий одно за другим, собирая результаты в список.

5 голосов
/ 02 мая 2011

liftM и mapM совершенно разные, как вы можете видеть по их типам и их реализации:

mapM         :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f as    =  sequence (map f as)

liftM        :: (Monad m) => (a1 -> r) -> m a1 -> m r
liftM f m1   = do { x1 <- m1; return (f x1) }

, так как mapM применяет монадическую функцию к каждому элементу списка, liftM применяет функцию в монадической настройке.

...