создаёт другую, бо́льшую коробку, которая посылает эти две коробки наружу для получения строк с терминала, а потом возвращает конкатенацию этих двух строк в качестве своего результата.
Выражение >(++) <$> 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]
Мы создаём функцию, которая вызовет функцию