52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 195

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 195

Например если мы скомпилируем такой код с флагом ddump-hi:

module Inline(f, g) where

g :: Int -> Int

g x = x + 2

f :: Int -> Int

f x = g $ g x

то среди прочих определений увидим:

ghc -c -ddump-hi -O Inline. hs

...

f :: GHC.Types.Int -> GHC.Types.Int

{- Arity: 1, HasNoCafRefs, Strictness: U(L)m,

Unfolding: InlineRule (1, True, False)

(\ x :: GHC.Types.Int ->

case x of wild { GHC.Types.I# x1 ->

GHC.Types.I# (GHC.Prim.+# (GHC.Prim.+# x1 2) 2) }) -}

...

В этом виде прочесть функцию не так просто. Ко всем именам добавлены имена модулей. Приведём

вывод к более простому виду с помощью флага dsuppress-all:

ghc -c -ddump-hi -dsuppress-all -O Inline. hs

...

f :: Int -> Int

{- Arity: 1, HasNoCafRefs, Strictness: U(L)m,

Unfolding: InlineRule (1, True, False)

(\ x :: Int -> case x of wild { I# x1 -> I# (+# (+# x1 2) 2) }) -}

...

Мы видим, что все вызовы функции g были заменены. Если вы всё же подозреваете, что GHC не справ-

ляется с встраиванием ваших часто используемых функций и это сказывается, попробуйте добавить к ним

INLINE, но при этом лучше узнать, привело ли это к росту производительности, проверить с помощью про-

филирования.

Отметим также прагму NOINLINE с её помощью мы можем запретить встраивание функции. Эта праг-

ма часто используется при различных трюках с unsafePerformIO, встраивание функции, которая содержит

неконтролируемые побочные эффекты, может повлиять на её результат.

Прагма RULES

Разработчики GHC хотели, чтобы их компилятор был расширяемым и программист мог бы определять

специфические для его приложения правила оптимизации. Для этого была придумана прагма RULES. За счёт

чистоты функций мы можем в очень простом виде выражать инварианты программы. Инвариант – это неко-

торое свойство значения, которое остаётся постоянным при некоторых преобразованиях. Наиболее распро-

странённые инварианты имеют собственные имена. Например, это коммутативность сложения:

forall a b. a + b = b + a

Здесь мы пишем: для любых a и b изменение порядка следования аргументов у (+) не влияет на результат.

С ключевым словом forall мы уже когда-то встречались, когда говорили о типе ST. Помните тип функции

runST? Пример свойства функции map:

forall f g.

map f . map g = map (f . g)

174 | Глава 10: Реализация Haskell в GHC

Это свойство принято называть дистрибутивностью. Мы видим, что функция композиции дистрибутив-

на относительно функции map. Инварианты определяют скрытые закономерности значений. За счёт чистоты

функций мы можем безболезненно заменить в любом месте программы левую часть на правую или наобо-

рот. Оптимизация начинается тогда, когда мы понимаем, что одна из частей может быть вычислена гораздо

эффективнее другой. Так в примере с map выражение справа от знака равно гораздо эффективнее, поскольку