52700.fb2
$ ./Rules
Cons 101 (Cons 102 (Cons 103 Nil))
Среди прочих правил, определённых в стандартных библиотеках, сработало и наше. Составим правила,
которые будут проводить оптимизацию слияние для наших списков, они будут заменять последовательное
применение mapL на один mapL c композицией функций, также промежуточный список будет устранён в
связке foldrL/mapL.
176 | Глава 10: Реализация Haskell в GHC
Прагма UNPACK
Наш основной враг на этапе оптимизации программы это лишние объекты кучи. Чем меньше объектов
мы создаём на пути к результату, тем эффективнее наша программа. С помощью прагмы INLINE мы можем
избавиться от многих объектов, связанных с вызовом функции, это объекты типа FUN. Прагма UNPACK позволя-
ет нам бороться с лишними объектами типа CON. В прошлой главе мы говорили о том, что значения в Haskell
содержат дополнительную служебную информацию, которая необходима на этапе вычисления, например
значение сначала было отложенным, потом мы до него добрались и вычислили, возможно оно оказалось не
определённым значением (undefined). Такие значения называются запакованными (boxed). Незапакованное
значение, это примитивное значение, как оно представлено в памяти компьютера. Вспомним определение
целых чисел:
data Int = I# Int#
По традиции все незапакованные значения пишутся с решёткой на конце. Запакованные значения позво-
ляют отклдывать вычисления, пользоваться undefined при определении функции. Но за эту гибкость прихо-
дится платить. Вспомним расход памяти в выражении [Pair 1 2]
nil = []
-- глобальный объект (не в счёт)
let x1
= I# 1
-- 2 слова
x2
= I# 2
-- 2 слова
p
= Pair x1 x2
-- 3 слова
val = Cons p nil
-- 3 слова
in
val
------------
-- 10 слов
Получилось десять слов для списка из одного элемента, который фактически хранит два значения. Размер
списка, который хранит такие пары будет зависеть от числа элементов N как 10 N. Тогда как полезная
нагрузка составляет 2 N. С помощью прагмы UNPACK мы можем отказаться от ленивой гибкости в пользу
меньшего расхода памяти. Эта прагма позволяет встраивать
один конструктор в поле другого. Это поле должно быть строгим (с пометкой ! ) и мономорфным (тип поля
должен быть конкретным типом, а не параметром), причём подчинённый тип должен содержать лишь один
конструктор (у него нет альтернатив):
data PairInt = PairInt
{-# UNPACK #-} !Int
{-# UNPACK #-} !Int
Мы конкретизировали поля Pair и сделали их строгими с помощью восклицательных знаков. После этого