Изучай Haskell во имя добра! (Липовача) - страница 189

Как здорово использовать аппликативный стиль со списками!

>ghci> (++) <$> ["хa","хeх","хм"] <*> ["?","!","."]

>["хa?","хa!","хa.","хeх?","хeх!","хeх.","хм?","хм!","хм."]

Ещё раз: мы использовали обычную функцию, принимающую две строки, между двумя списками строк, просто вставляя соответствующие аппликативные операторы.

Вы можете воспринимать списки как недетерминированные вычисления. Значение вроде >100 или >"что" можно рассматривать как детерминированное вычисление, которое имеет только один результат. В то же время список вроде >[1,2,3] можно рассматривать как вычисление, которое не в состоянии определиться, какой результат оно желает иметь, поэтому возвращает нам все возможные результаты. Поэтому когда вы пишете что-то наподобие >(+) <$> [1,2,3] <*> [4,5,6], то можете рассматривать это как объединение двух недетерминированных вычислений с помощью оператора >+ только для того, чтобы создать ещё одно недетерминированное вычисление, которое ещё меньше уверено в своём результате.

Использование аппликативного стиля со списками часто является хорошей заменой генераторам списков. В главе 1 мы хотели вывести все возможные комбинации произведений >[2,5,10] и >[8,10,11] и с этой целью предприняли следующее:

>ghci> [x*y | x <– [2,5,10], y <– [8,10,11]]

>[16,20,22,40,50,55,80,100,110]

Мы просто извлекаем значения из обоих списков и применяем функцию между каждой комбинацией элементов. То же самое можно сделать и в аппликативном стиле:

>ghci> (*) <$> [2,5,10] <*> [8,10,11]

>[16,20,22,40,50,55,80,100,110]

Для меня такой подход более понятен, поскольку проще понять, что мы просто вызываем оператор >* между двумя недетерминированными вычислениями. Если бы мы захотели получить все возможные произведения элементов, больших 50, мы бы использовали следующее:

>ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]

>[55,80,100,110]

Легко увидеть, что вызов выражения >pure f <*> xs при использовании списков эквивалентен выражению >fmap f xs. Результат вычисления >pure f – это просто >[f], а выражение >[f] <*> xs применит каждую функцию в левом списке к каждому значению в правом; но в левом списке только одна функция, и, следовательно, это похоже на отображение.

Тип IO – тоже аппликативный функтор

Другой экземпляр класса >Applicative, с которым мы уже встречались, – экземпляр для типа >IO. Вот как он реализован:

>instance Applicative IO where

>   pure = return

>   a <*> b = do

>      f <– a

>      x <– b

>      return (f x)

Поскольку суть функции >pure состоит в помещении значения в минимальный контекст, который всё ещё содержит значение как результат, логично, что в случае с типом