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

создаёт другую, бо́льшую коробку, которая посылает эти две коробки наружу для получения строк с терминала, а потом возвращает конкатенацию этих двух строк в качестве своего результата.

Выражение >(++) <$> getLine <*> getLine имеет тип >IO String. Это означает, что данное выражение является совершенно обычным действием ввода-вывода, как и любое другое, тоже возвращая результирующее значение, подобно другим действиям ввода-вывода. Вот почему мы можем выполнять следующие вещи:

>main = do

>   a <– (++) <$> getLine <*> getLine

>   putStrLn $ "Две строки, соединённые вместе: " ++ a

Функции в качестве аппликативных функторов

Ещё одним экземпляром класса >Applicative является тип >(–>) r, или функции. Мы нечасто используем функции в аппликативном стиле, но концепция, тем не менее, действительно интересна, поэтому давайте взглянем, как реализован экземпляр функции[12].

>instance Applicative ((–>) r) where

>   pure x = (\_ –> x)

>   f <*> g = \x –> f x (g x)

Когда мы оборачиваем значение в аппликативное значение с помощью функции >pure, результат, который оно возвращает, должен быть этим значением. Минимальный контекст по умолчанию по-прежнему возвращает это значение в качестве результата. Вот почему в реализации экземпляра функция >pure принимает значение и создаёт функцию, которая игнорирует передаваемый ей параметр и всегда возвращает это значение. Тип функции >pure для экземпляра типа >(–>) r выглядит как >pure :: a –> (r –> a).

>ghci> (pure 3) "ля"

>3

Из-за каррирования применение функции левоассоциативно, так что мы можем опустить скобки:

>ghci> pure 3 "ля"

>3

Реализация экземпляра ><*> немного загадочна, поэтому давайте посмотрим, как использовать функции в качестве аппликативных функторов в аппликативном стиле:

>ghci> :t (+) <$> (+3) <*> (*100)

>(+) <$> (+3) <*> (*100) :: (Num a) => a –> a

>ghci> (+) <$> (+3) <*> (*100) $ 5

>508

Вызов оператора ><*> с двумя аппликативными значениями возвращает аппликативное значение, поэтому если мы вызываем его с двумя функциями, то получаем функцию. Что же здесь происходит? Когда мы выполняем >(+) <$> (+3) <*> (*100), мы создаём функцию, которая применит оператор + к результатам выполнения функций >(+3) и >(*100) и вернёт это значение. При вызове выражения >(+) <$> (+3) <*> (*100) $ 5 функции >(+3) и >(*100) сначала применяются к значению 5, что в результате даёт 8 и 500; затем оператор >+ вызывается со значениями 8 и 500, что в результате даёт 508.

Следующий код аналогичен:

>ghci> (\x y z –> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5

>[8.0,10.0,2.5]

Мы создаём функцию, которая вызовет функцию