Так что это вроде fmap, только сама функция находится в контексте. Нам нужно каким-то образом извлечь её из контекста и с её помощью отобразить значение f a, а затем вновь собрать контекст. Поскольку все функции в языке Haskell по умолчанию каррированы, мы можем использовать сочетание из операций <$> и <*> между аппликативными значениями, чтобы применять функции, принимающие несколько параметров.

Однако, оказывается, как и функция fmap, операция <*> тоже может быть реализована, используя лишь то, что даёт нам класс типов Monad. Функция ap, по существу, – это <*>, только с ограничением Monad, а не Applicative. Вот её определение:

ap :: (Monad m) => m (a –> b) –> m a –> m b

ap mf m = do

   f <– mf

   x <– m

   return (fx)

Функция ap – монадическое значение, результат которого – функция. Поскольку функция, как и значение, находится в контексте, мы берём функцию из контекста и называем её f, затем берём значение и называем его x, и, в конце концов, применяем функцию к значению и представляем это в качестве результата. Вот быстрая демонстрация:

ghci> Just (+3) <*> Just 4

Just 7

ghci> Just (+3) `ap` Just 4

Just 7

ghci> [(+1),(+2),(+3)] <*> [10,11]

[11,12,12,13,13,14]

ghci> [(+1),(+2),(+3)] `ap` [10,11]

[11,12,12,13,13,14]

Теперь нам видно, что монады настолько же сильны, насколько и аппликативные функторы, потому что мы можем использовать методы класса Monad для реализации функций из класса Applicative. На самом деле, когда обнаруживается, что определённый тип является монадой, зачастую сначала записывают экземпляр класса Monad, а затем создают экземпляр класса Applicative, просто говоря, что функция pure – это return, а операция <*> – это ap. Аналогичным образом, если у вас уже есть экземпляр класса Monad для чего-либо, вы можете сделать для него экземпляр класса Functor, просто говоря, что функция fmap – это liftM.

Функция liftA2 весьма удобна для применения функции между двумя аппликативными значениями. Она определена вот так:

liftA2 :: (Applicative f) => (a –> b –> c) –> f a –> f b –> f c

liftA2 f x y = f <$> x <*> y

Функция liftM2 делает то же, но с использованием ограничения Monad. Есть также функции liftM3, liftM4 и liftM5.

Вы увидели, что монады не менее сильны, чем функторы и аппликативные функторы – и, хотя все монады, по сути, являются функторами и аппликативными функторами, у них необязательно имеются экземпляры классов Functor и Applicative. Мы изучили монадические эквиваленты функций, которые используются функторами и аппликативными функторами.

<p>Функция join</p>

Есть кое-какая пища для размышления: если результат монадического значения – ещё одно монадическое значение (одно монадическое значение вложено в другое), можете ли вы «разгладить» их до одного лишь обычного монадического значения? Например, если у нас есть Just (Just 9), можем ли мы превратить это в Just 9? Оказывается, что любое вложенное монадическое значение может быть разглажено, причём на самом деле это свойство уникально для монад. Для этого у нас есть функция join. Её тип таков:

join :: (Monad m) => m (m a) –> m a

Значит, функция join принимает монадическое значение в монадическом значении и отдаёт нам просто монадическое значение; другими словами, она его разглаживает. Вот она с некоторыми значениями типа Maybe:

ghci> join (Just (Just 9))

Just 9

ghci> join (Just Nothing)

Nothing

ghci> join Nothing

Nothing

В первой строке – успешное вычисление как результат успешного вычисления, поэтому они оба просто соединены в одно большое успешное вычисление. Во второй строке значение Nothing представлено как результат значения Just. Всякий раз, когда мы раньше имели дело со значениями Maybe и хотели объединить несколько этих значений – будь то с использованием операций <*> или >>= – все они должны были быть значениями конструктора Just, чтобы результатом стало значение Just. Если на пути возникала хоть одна неудача, то и результатом являлась неудача; нечто аналогичное происходит и здесь. В третьей строке мы пытаемся разгладить то, что возникло вследствие неудачи, поэтому результат – также неудача.

Разглаживание списков осуществляется довольно интуитивно:

ghci> join [[1,2,3],[4,5,6]]

[1,2,3,4,5,6]

Перейти на страницу:

Похожие книги