константы, которые можно подставлять в другие функции. В этом разделе мы немного потренируемся в пе-
регрузке численных значений и превратим числа в функции, функции и в самом деле станут константами.
Мы определим экземпляр Num
для функций, которые возвращают числа. Смысл этих операций заключается втом, что теперь мы применяем обычные операции сложения умножения к функциям, аргумент которых сов-
падает по типу. Например для того чтобы умножить функции \t ->
t+2 и \t -> t+3 мы составляем новуюфункцию \t ->
(t+2) * (t+3), которая получает на вход значение t применяет его к каждой из функций изатем умножает результаты:
78 | Глава 5: Функции высшего порядка
module FunNat where
import Prelude
(Show(.. ), Eq(.. ), Num(.. ), error)instance Show
(t -> a) whereshow _ = error
”Sorry, no show. It’s just for Num”instance Eq
(t -> a) where(==
) _ _ = error ”Sorry, no Eq. It’s just for Num”instance Num
a => Num (t -> a) where(+
) = fun2 (+)(*
) = fun2 (*)(-
) = fun2 (-)abs
=
fun1 abssignum
=
fun1 signumfromInteger =
const . fromIntegerfun1 ::
(a -> b) -> ((t -> a) -> (t -> b))fun1 =
(. )fun2 ::
(a -> b -> c) -> ((t -> a) -> (t -> b) -> (t -> c))fun2 op a b =
\t -> a t ‘op‘ b tФункции fun1 и fun2 превращают функции, которые принимают значения, в функции, которые прини-
мают другие функции.
Из-за контекста класса Num
нам пришлось объявить два фиктивных экземпляра для классов Show и Eq.Загрузим модуль FunNat
в интерпретатор и посмотрим что же у нас получилось:Prelude> :
l FunNat. hs[1 of
1] Compiling FunNat( FunNat.
hs, interpreted )Ok
, modules loaded: FunNat.*FunNat>
2 22
*FunNat>
2 52
*FunNat>
(2 + (+1)) 03
*FunNat>
((+2) * (+3)) 112
На первый взгляд кажется что выражение 2 2 не должно пройти проверку типов, ведь мы применяем
значение к константе. Но на самом деле 2 это не константа, а значение 2 :: Num
a => a и подспудно к двойкеприменяется функция fromInteger. Поскольку в нашем модуле мы определили экземпляр Num
для функций,второе число 2 было конкретизировано по умолчанию до Integer
, а первое число 2 было конкретизированодо Integer -> Integer
. Компилятор вывел из контекста, что под 2 мы понимаем функцию. Функция быласоздана с помощью метода fromInteger. Эта функция принимает любое значение и возвращает двойку.
Далее мы складываем и перемножаем функции словно это обычные значения. Что интересно мы можем
составлять и такие выражения:
*FunNat> let
f = ((+) - (*))*FunNat>
f 1 21
Как была вычислена эта функция? Мы определили экземпляр функций для значений типа Num
a => t->
a. Если мы вспомним, что функция двух аргументов на самом деле является функцией одного аргумента:Num
a => t1 -> (t2 -> a), мы заметим, что тип Num a => (t2 -> a) принадлежит Num, теперь если мыобозначим его за a’, то мы получим тип Num
a’ => t1 -> a’, это совпадает с нашим исходным экземпляром.Получается, что за счёт механизма частичного применения мы одним махом определили экземпляры Num
для функций
Итак функция f имеет вид:
\t1 t2 ->
(t1 + t2) - (t1 * t2)Подставим значения:
Функциональный калькулятор | 79
(\t1 t2 ->
(t1 + t2) - (t1 * t2)) 1 2(\t2 ->
(1 + t2) - (1 * t2) 2(1 +
2) - (1 * 2)3 -
21
Теперь давайте составим несколько выражений с обобщёнными функциями. Для этого добавим в модуль
FunNat
директиву импорта функций из модуля Data.Function. Также добавим несколько основных функцийдля списков и класс Ord
:module FunNat where
import Prelude
(Show(.. ), Eq(.. ), Ord(.. ), Num(.. ), error)import Data.Function
(id, const, (. ), ($), flip, on)import Prelude
(map, foldr, filter, zip, zipWith)...
и загрузим модуль в интерпретатор:
Prelude> :
load FunNat[1 of
1] Compiling FunNat( FunNat.
hs, interpreted )Ok
, modules loaded: FunNat.Составим функцию, которая принимает один аргумент, умножает его на два, вычитает 10 и берёт модуль
числа.
*FunNat> let
f = abs $ id * 2 - 10*FunNat>
f 26
*FunNat>
f 1010
Давайте посмотрим как была составлена эта функция:
abs $
id * 2 - 10=>
abs $
(id * 2) - 10-- приоритет умножения
=>
abs $
(\x -> x * \x -> 2) - 10-- развернём id и 2
=>
abs $
(\x -> x * 2) - 10-- по определению (*) для функций
=>
abs $
(\x -> x * 2) - \x -> 10-- развернём 10
=>
abs $
\x -> (x * 2) - 10-- по определению (-) для функций
=>
\x ->
abs x . \x -> (x * 2) - 10-- по определению abs для функций
=>
\x ->
abs ((x * 2) - 10)-- по определению (.)
=>
\x ->
abs ((x * 2) - 10)Функция возведения в квадрат:
*FunNat> let
f = id * id*FunNat>
map f [1,2,3,4,5][1,4,9,16,25]
*FunNat>
map (id * id - 1) [1,2,3,4,5][0,3,8,15,24]
Обратите внимание на краткость записи. В этом выражении (id *
id - 1) проявляется основное пре-