Я читал статью, написанную Саймоном Пейтоном Джонсом и др. названный "Игра по правилам: переписывание как практическая техника оптимизации в GHC" . Во втором разделе, а именно "Основная идея", они пишут:
Рассмотрим знакомую функцию
map
, которая применяет функцию к каждому элементу списка. Написанная в Haskell,map
выглядит следующим образом:
map f [] = []
map f (x:xs) = f x : map f xs
Теперь предположим, что компилятор встречает следующий вызов
map
:
map f (map g xs)
Мы знаем, что это выражение эквивалентно
map (f . g) xs
(где "." - состав функции), и мы знаем, что последнее выражение более эффективно, чем первое, потому что нет промежуточного списка. Но компилятор не имеет таких знаний.
Одно из возможных возражений заключается в том, что компилятор должен быть умнее, но программист всегда будет знать то, что компилятор не может понять. Еще одно предложение: позволить программисту передавать такие знания непосредственно компилятору. Это направление, которое мы исследуем здесь.
Мой вопрос: почему мы не можем сделать компилятор умнее? Авторы говорят, что "но программист всегда будет знать то, что компилятор не может понять". Однако это недействительный ответ, потому что компилятор действительно может понять, что map f (map g xs)
эквивалентен map (f . g) xs
, и вот как это сделать:
map f (map g xs)
-
map g xs
объединяется сmap f [] = []
.Следовательно,
map g [] = []
. -
map f (map g []) = map f []
.map f []
объединяется сmap f [] = []
.Следовательно,
map f (map g []) = []
. -
map g xs
объединяется сmap f (x:xs) = f x : map f xs
.Следовательно,
map g (x:xs) = g x : map g xs
. -
map f (map g (x:xs)) = map f (g x : map g xs)
.map f (g x : map g xs)
объединяется сmap f (x:xs) = f x : map f xs
.Следовательно,
map f (map g (x:xs)) = f (g x) : map f (map g xs)
.
Следовательно, теперь мы имеем правила:
map f (map g []) = []
map f (map g (x:xs)) = f (g x) : map f (map g xs)
Как вы видите, f (g x)
- это просто (f . g)
и map f (map g xs)
вызывается рекурсивно. Это точно определение map (f . g) xs
. Алгоритм для этого автоматического преобразования кажется довольно простым. Так почему бы не реализовать это вместо правил перезаписи?