Применение функций с помощью оператора $

Пойдём дальше. Теперь объектом нашего внимания станет оператор $, также называемый аппликатором функций. Прежде всего посмотрим, как он определяется:

($) :: (a –> b) –> a –> b

f $ x = f x

Зачем? Что это за бессмысленный оператор? Это просто применение функции! Верно, почти, но не совсем!.. В то время как обычное применение функции (с пробелом) имеет высший приоритет, оператор $ имеет самый низкий приоритет. Применение функции с пробелом левоассоциативно (то есть f a b c i – это то же самое, что (((f a) b) c)), в то время как применение функции при помощи оператора $ правоассоциативно.

Всё это прекрасно, но нам-то с того какая польза? Прежде всего оператор $ удобен тем, что с ним не приходится записывать много вложенных скобок. Рассмотрим выражение sum (map sqrt [1..130]). Поскольку оператор $ имеет самый низкий приоритет, мы можем переписать это выражение как sum $ map sqrt [1..130], сэкономив драгоценные нажатия на клавиши. Когда в функции встречается знак $, выражение справа от него используется как параметр для функции слева от него. Как насчёт sqrt 3 + 4 + 9? Здесь складываются 9, 4 и корень из 3. Если мы хотим получить квадратный корень суммы, нам надо написать sqrt (3 + 4 + 9) – или же (в случае использования оператора $) sqrt $ 3 + 4 + 9, потому что у оператора $ низший приоритет среди всех операторов. Вот почему вы можете представить символ $ как эквивалент записи открывающей скобки с добавлением закрывающей скобки в крайней правой позиции выражения.

Посмотрим ещё на один пример:

ghci> sum (filter (> 10) (map (*2) [2..10]))

80

Очень много скобок, даже как-то уродливо. Поскольку оператор $ правоассоциативен, выражение f (g (z x)) эквивалентно записи f $ g $ z x. Поэтому пример можно переписать:

sum $ filter (> 10) $ map (*2) [2..10]

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

ghci> map ($ 3) [(4+), (10*), ( 2), sqrt]

[7.0,30.0,9.0,1.7320508075688772]

Функция ($ 3) применяется к каждому элементу списка. Если задуматься о том, что она делает, то окажется, что она берёт функцию и применяет её к числу 3. Поэтому в данном примере каждая функция из списка применится к тройке, что, впрочем, и так очевидно.