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

.

Печать функций

До сих пор мы давали частично применённым функциям имена, после чего добавляли недостающие параметры, чтобы всё-таки посмотреть на результаты. Однако мы ни разу не попробовали напечатать сами функции. Попробуем? Что произойдёт, если мы попробуем выполнить >multThree 3 4 в GHCi вместо привязки к имени с помощью ключевого слова >let либо передачи другой функции?

>ghci> multThree 3 4

>:1:0:

>  No instance for (Show (a –> a))

>    arising from a use of `print' at :1:0–12

>  Possible fix: add an instance declaration for (Show (a –> a))

>  In the expression: print it

>  In a 'do' expression: print it

GHCi сообщает нам, что выражение порождает функцию типа >a –> a, но он не знает, как вывести её на экран. Функции не имеют экземпляра класса >Show, так что мы не можем получить точное строковое представление функций. Когда мы вводим, скажем, >1 + 1 в терминале GHCi, он сначала вычисляет результат (>2), а затем вызывает функцию >show для >2, чтобы получить текстовое представление этого числа. Текстовое представление >2 – это строка >"2", которая и выводится на экран.

ПРИМЕЧАНИЕ. Удостоверьтесь в том, что вы поняли, как работает каррирование и частичное применение функций, поскольку эти понятия очень важны.

Немного о высоких материях

Функции могут принимать функции в качестве параметров и возвращать функции в качестве значений. Чтобы проиллюстрировать это, мы собираемся создать функцию, которая принимает функцию, а затем дважды применяет её к чему-нибудь!

>applyTwice :: (a –> a) –> a –> a

>applyTwice f x = f (f x)



Прежде всего, обратите внимание на объявление типа. Раньше мы не нуждались в скобках, потому что символ >–> обладает правой ассоциативностью. Однако здесь скобки обязательны. Они показывают, что первый параметр – это функция, которая принимает параметр некоторого типа и возвращает результат того же типа. Второй параметр имеет тот же тип, что и аргумент функции – как и возвращаемый результат. Мы можем прочитать данное объявление в каррированном стиле, но, чтобы избежать головной боли, просто скажем, что функция принимает два параметра и возвращает результат. Первый параметр – это функция (она имеет тип >a –> a), второй параметр имеет тот же тип >a. Заметьте, что совершенно неважно, какому типу будет соответствовать типовая переменная >a>Int, >String или вообще чему угодно – но при этом все значения должны быть одного типа.

ПРИМЕЧАНИЕ. Отныне мы будем говорить, что функция принимает несколько параметров, вопреки тому что в действительности каждая функция принимает только один параметр и возвращает частично применённую функцию. Для простоты будем говорить, что