Показаны сообщения с ярлыком ghc. Показать все сообщения
Показаны сообщения с ярлыком ghc. Показать все сообщения

вторник, 14 октября 2014 г.

Как я обновлял ghc до версии 7.8 в Fedora 20

Официально в Fedora до сих пор старые ghc-7.6.3 и haskell-platform-2013.2.0.0. И по всей видимости они останутся в следующей Fedora 21 (см. здесь). Однако, выход есть, и для этого не нужно собирать ghc с нуля. Достаточно установить репозитории ghc-7.8 и cabal-install от Йенса Петерсена — мэйнтейнера haskell в Fedora (кстати, у него есть и другие интересные репозитории, связанные с haskell: статическая сборка pandoc, функциональный язык idris, vim-подобный редактор yi — все это можно найти по этой ссылке). Поскольку у меня уже стояли ghc и haskell-platform, то первое, что я сделал — это удалил их из системы.
yum remove ghc* haskell-platform
Затем настроил репозитории Йенса и установил ghc и cabal-install.
yum install ghc cabal-install
Заметьте — пакета-коллекции haskell-platform в репозитории ghc-7.8 нет, да он и не нужен, поскольку все пакеты можно установить с помощью cabal. Естественно, я и раньше использовал cabal для установки пакетов haskell и делал это с опцией --global, соответственно в директории /usr/local/lib/ скопилось множество директорий типа conduit-1.1.6/, содержащих в себе единственную поддиректорию ghc-7.6.3/ с данными пакетов. Новый ghc, как выяснилось позже, устанавливает все пакеты в единую директорию /usr/local/lib/x86_64-linux-ghc-7.8.3/ (имя последней поддиректории, очевидно, зависит от системы), поэтому старые пакетные директории следовало удалить. Вот команда, которая позволяет перечислить все пакетные директории:
find /usr/local/lib -mindepth 2 -maxdepth 2 -type d -name 'ghc-7*' | xargs dirname
Теперь требуется убедиться, что список не содержит неожиданных верхнеуровневых директорий типа /usr/local/lib, потому что сейчас мы будем их удалять! Я постарался максимально обезопасить команду find с помощью опций -mindepth и -maxdepth, поэтому появление неожиданных директорий в списке очень маловероятно. Итак, удаляем пакетные директории.
find /usr/local/lib -mindepth 2 -maxdepth 2 -type d -name 'ghc-7*' | xargs dirname | xargs rm -rf
Также удаляем директории .ghc/ и .cabal/ в домашних директориях пользователя и суперпользователя (так как я устанавливал пакеты от имени суперпользователя с опцией --global), но делаем это осторожно, вспоминая, нет ли там каких-нибудь персональных настроек. У меня все персональные настройки находятся в файле .ghci в домашней директории, поэтому удаление этих директорий не могло привести к потере настроек. На этом этапе мы еще не закончили с удалениями. Данные о пакетах все еще хранятся в директории /usr/lib64/ghc-7.6.3/package.conf.d/, в этом можно убедиться, выполнив команду
ghc --info | grep "Global Package DB"
Хотя после установки нового ghc эта команда покажет новую поддиректорию ghc-7.8.3/, старая директория будет по-прежнему находиться в системе. Удаляем проблемные пакеты из реестра (хотя сейчас я не уверен, что этот шаг вообще был нужен, поскольку Global Package DB указывает на новую директорию):
for p in `ghc-pkg check 2>&1 | grep problems | awk '{print $6}' | sed -e 's/:$//'` ; do ghc-pkg unregister $p --force ; done
(этот скрипт я позаимствовал отсюда) и физически удаляем директорию /usr/lib64/ghc-7.6.3/:
rm -rf /usr/lib64/ghc-7.6.3
Вот теперь все готово для установки новых пакетов. Лично я устанавливал пакеты pandoc, happy, alex, hlint, MissingH, ghc-vis и lens с опцией --global.
cabal update
...
cabal install pandoc --global
...
cabal install happy --global
...
cabal install alex --global
...
cabal install hlint --global
...
cabal install MissingH --global
...
cabal install gtk2hs-buildtools --global
...
yum install librsvg2-devel
...
cabal install ghc-vis --global
...
cabal install -ftransformer3 transformers-compat --global
...
cabal install --constraint="transformers==0.3.0.0" lens --global
...
Я заменил выводы команд многоточиями, поскольку они многословны и малоинтересны. Порядок установки пакетов может иметь значение! Для установки ghc-vis потребовалось установить пакет gtk2hs-buildtools и системный пакет librsvg2-devel. После установки ghc-vis имеет смысл скопировать содержимое файла /usr/local/share/ghc-vis-0.7.2.3/ghci в домашний .ghci. Установка пакета lens выглядит необычно, но без этих опций его не установить, собственно решение я нашел здесь. Во время установки пакетов вы возможно заметите такие предупреждения:
<no location info>:
    Warning: Couldn't figure out linker information!
             Make sure you're using GNU gcc, or clang
Точно такие же сообщения вы увидите при компиляции любой программы новым ghc. Но это только в том случае, если ваша локаль не является английской (моя локаль русская). Я долго искал в чем здесь проблема и таки нашел, вот этот баг на трекере ghc. В общем-то ничего опасного: ghc запускает команду gcc -v, ищет в выводе строку gcc version ... и просто выводит предупреждение, если ее не находит. В русской локали gcc выводит строку gcc версия .... Лично меня раздражают эти бессмысленные предупреждения, обновление ghc с исправлением выйдет не скоро, поэтому я просто добавил в .bashrc новый алиас ghc-make.
alias ghc-make='LANG=C ghc --make'
Да, кстати. В ghc 7.8 появилась возможность настроить prompt2 — приглашение командной строки ghci для многострочных команд. Лично я выделяю приглашения разными цветами, вот мои настройки в .ghci:
:set prompt "\ESC[38;5;208m%s> \ESC[m"
:set prompt2 "\ESC[38;5;221m%s| \ESC[m"

понедельник, 15 сентября 2014 г.

Вью-паттерны (view patterns) в haskell на конкретном примере

Как перевести на русский язык view patterns я, честно говоря, не знаю. Образцы с представлением? Образцы с функциональным преобразованием? Чтобы случайно не исказить смысл этих объектов, я буду называть их просто вью-паттернами. Хотя смысл, в общем-то, простой: части общего образца заменяются вызовами функций, которые возвращают объекты произвольного типа — эти объекты представлены в виде образцов, элементы которых, как обычно, связываются с переменными в теле основной функции. Такие функции называются view, а образцы, на которые они указывают (с помощью стрелочки ->) — view patterns. Подробнее о вью-паттернах можно узнать здесь. Вот конкретный пример. Возьмем многострадальный фильтр для pandoc. В нем есть (или уже был, если я решу закоммитить новое решение) участок
substInlineStyle :: Format -> MMap -> Inline -> Inline
substInlineStyle fm m i@(Image ((Style style):alt) (src, title)) =
    substInlineStyle' fm m style (alt, src, title) i
substInlineStyle fm m i@(Link ((Style style):alt) (src, title)) =
    substInlineStyle' fm m style (alt, src, title) i
substInlineStyle _ _ i = i

substInlineStyle' :: Format -> MMap -> String -> ObjParams -> Inline -> Inline
substInlineStyle' (Format fm) m style params i
    | Just (MetaMap mm) <- M.lookup style m
    , Just (MetaBlocks [Para ((RawInline f s):r)]) <- M.lookup fm mm =
        let subst (Style "ALT") = RawInline f "$ALT$"
            subst i = i
        in RawInline f $ substParams fm params $
                                s ++ stringify' fm (map subst r)
    | otherwise = i
Что мне здесь не нравится? Первый и второй клозы функции substInlineStyle отличаются только именем конструктора в образце (Image и Link), больше эти имена нигде не фигурируют. Оба конструктора имеют одинаковые аргументы [Inline] Target (так они определены в pandoc). Так почему бы не избавиться от этих конструкторов таким образом, чтобы объединить первый и второй клозы substInlineStyle в одну функцию? Однако, плейсхолдеры конструкторов в образцах в haskell не используются, охранные выражения с образцами (см. предыдущую статью) здесь тоже бесполезны (они всё так же не смогут убрать имена конструкторов или заменить их плейсхолдерами). А чем здесь могут помочь вью-паттерны? Ну, они могут заменить тип Inline с его конструкторами Image и Link на какой-нибудь новый тип, например, кортеж из двух аргументов этих конструкторов, обернутый в Maybe!
substInlineStyle :: Format -> MMap -> Inline -> Inline
substInlineStyle (Format fm) m
                 i@(getInlineParams -> Just (((Style style):alt), (src, title)))
    | Just (MetaMap mm) <- M.lookup style m
    , Just (MetaBlocks [Para ((RawInline f s):r)]) <- M.lookup fm mm =
        let params = (alt, src, title)
            subst (Style "ALT") = RawInline f "$ALT$"
            subst i = i
        in RawInline f $ substParams fm params $
                                s ++ stringify' fm (map subst r)
    | otherwise = i
substInlineStyle _ _ i = i

getInlineParams :: Inline -> Maybe ([Inline], Target)
getInlineParams (Image alt target) = Just (alt, target)
getInlineParams (Link alt target) = Just (alt, target)
getInlineParams _ = Nothing
Еще один вариант — искать стиль сразу в getInlineParams и возвращать из нее кортеж из трех элементов — стиля, списка alt и элемента target.
substInlineStyle :: Format -> MMap -> Inline -> Inline
substInlineStyle (Format fm) m
                 i@(getInlineParams -> Just (Style style, alt, (src, title)))
    | Just (MetaMap mm) <- M.lookup style m
    , Just (MetaBlocks [Para ((RawInline f s):r)]) <- M.lookup fm mm =
        let params = (alt, src, title)
            subst (Style "ALT") = RawInline f "$ALT$"
            subst i = i
        in RawInline f $ substParams fm params $
                                s ++ stringify' fm (map subst r)
    | otherwise = i
substInlineStyle _ _ i = i

getInlineParams :: Inline -> Maybe (Inline, [Inline], Target)
getInlineParams (Image (style@(Style _):alt) target) = Just (style, alt, target)
getInlineParams (Link (style@(Style _):alt) target) = Just (style, alt, target)
getInlineParams _ = Nothing
Вот и всё! Нам удалось выбросить имена Image и Link из substInlineStyle и благодаря этому объединить два ее клоза в один. Правда, пришлось добавить новую вью-функцию getInlineParams, но в любом случае возможности и семантика нового решения стали намного богаче. Во-первых, вью-функцию можно спрятать в библиотеку (или же, в общем случае, эта функция может быть предоставлена библиотекой, либо являться стандартной), во-вторых, вью-функция может производить важные семантические изменения данных, а не просто выдергивать данные из конструкторов, как в нашем случае. Вот просто потрясающий пример, иллюстрирующий второе утверждение (найден здесь):
endpoints (sort -> begin : (reverse -> end : _)) = Just (begin, end)
endpoints _ = Nothing
Эта штука находит минимальный и максимальный элементы в произвольном списке, предварительно сортируя этот список прямо в образце! А представьте что будет, если это скрестить с синонимами образцов!
pattern Sort begin end <- (sort -> begin : (reverse -> end : _))

endpoints (Sort begin end) = Just (begin, end)
endpoints _ = Nothing

min (Sort begin _) = Just begin
min _ = Nothing

max (Sort _ end) = Just end
max _ = Nothing
Сплошное очарование. Обратите внимание: несмотря на то, что функции endpoints, min и max ожидают на входе список, переменные их образцов связаны с конкретными элементами списка — концами begin и end. Осталось сказать, что поскольку вью-паттерны являются нестандартным расширением, то для их использования в начало программы следует добавить прагму
{-# LANGUAGE ViewPatterns #-}
, либо при компиляции указать опцию -XViewPatterns.

пятница, 12 сентября 2014 г.

Охранные выражения с образцом (pattern guards) в haskell на конкретном примере

Я опять переписал фильтр для pandoc, о котором рассказывал здесь и здесь. Давайте посмотрим на оригинальное определение первого клоза функции substStyle.
substStyle (Format fm) m b@(Para [Image ((Style style):alt) (src, title)]) =
    case M.lookup style m of
        Nothing -> b
        Just (MetaMap mm) ->
            let params = (alt, src, title)
            in case M.lookup fm mm of
                   Nothing -> b
                   Just (MetaBlocks [RawBlock f s]) ->
                       RawBlock f $ substParams fm params s
                   Just (MetaBlocks [mb]) ->
                       walk substParams' mb
                       where substParams' (RawInline f s) =
                                   RawInline f $ substParams fm params s
                             substParams' i = i
                   Just _ -> b
        Just _ -> b
Смысл этой функции простой: вернуть новое форматирование в случае, если имя style было найдено в списке объектов метаданных документа (результат Just (MetaMap mm) в первом выражении case) и представление найденного объекта метаданных соответствует MetaBlocks [RawBlock f s] (для встроенного кода latex) или просто MetaBlocks [mb] (для встроенного кода html) как результат проверки второго case. Несмотря на то, что нас фактически интересуют только эти два условия (в остальных случаях мы просто вернем исходный блок b), использование вложенных case привело к формированию неоправданно разросшегося куста с шестью ветвями, четыре из которых в общем-то ничего полезного не делают (просто возвращают b). Как бы нам подрезать ненужные ветви? Оказывается, в haskell есть механизм, который позволит решить эту задачу. Это охранные выражения с образцом (я попытался перевести pattern guards без искажения семантики этих объектов). Документация доступна здесь. Несмотря на то, что охранные выражения с образцом являются синтаксическим расширением, никакие дополнительные флаги компиляции для их использования не требуются, поскольку они были включены в стандарт Haskell 2010. В упомянутой выше документации приводится пример с двумя последовательными поисками в отображении: как раз то, что нам нужно. Вот результат трансляции кейсов из substStyle в охранные выражения.
substStyle (Format fm) m b@(Para [Image ((Style style):alt) (src, title)])
    | Just (MetaMap mm) <- M.lookup style m
    , Just (MetaBlocks [mb]) <- M.lookup fm mm =
        let params = (alt, src, title)
            substStyle' mb
                | RawBlock f s <- mb =
                    RawBlock f $ substParams fm params s
                | otherwise =
                    let substParams' (RawInline f s) =
                                RawInline f $ substParams fm params s
                        substParams' i = i
                    in walk substParams' mb
        in substStyle' mb
    | otherwise = b
Итак, новая функция substStyle реализована с помощью охранных выражений с образцом, в которых последовательно ищутся значение style в метаданных документа и (на основе уже найденного объекта-стиля mm!) представление стиля в метаданных. Если представление стиля соответствует MetaBlocks [mb], то, с помощью функции substStyle’, которая тоже реализована с помощью охранных выражений с образцом, выполняется подстановка шаблона стиля в документ. Хочу также отметить, что в данном случае let-выражения нельзя заменить словом where, поскольку последнее может быть использовано только после перечисления всех охранных выражений функции, таким образом замена внешнего let на where приведет просто к ошибке компиляции, ну а в случае с внутренним let его замена на where слегка изменит семантику: программа в общем-то скомпилируется, поскольку where будет стоять после всех охранных выражений, однако все объявления внутри него (то есть функция substParams’) будут видны во всех ветвях охранных выражений — а зачем нам это надо? Оставшиеся трансляции case в pattern guards. Из
substStyle (Format fm) m b@(Para cnt) =
    let walk' = walk $ substInlineStyle (Format fm) m
    in case M.lookup "para_style" m of
           Nothing -> walk' b
           Just (MetaMap mm) ->
               case M.lookup fm mm of
                   Nothing -> walk' b
                   Just (MetaBlocks [Para [Span attr _]]) ->
                       walk' $ Plain [Span attr cnt]
                   Just _ -> walk' b
           Just _ -> walk' b
в
substStyle (Format fm) m b@(Para cnt)
    | Just (MetaMap mm) <- M.lookup "para_style" m
    , Just (MetaBlocks [Para [Span attr _]]) <- M.lookup fm mm =
        walk' $ Plain [Span attr cnt]
    | otherwise = walk' b
    where walk' = walk $ substInlineStyle (Format fm) m
(здесь мы использовали where, поскольку объявленная в нем функция walk’ должна быть доступна во всех ветвях охранных выражений). Из
substInlineStyle' (Format fm) m style params i =
    case M.lookup style m of
        Nothing -> i
        Just (MetaMap mm) ->
            case M.lookup fm mm of
                Nothing -> i
                Just (MetaBlocks [Para ((RawInline f s):r)]) ->
                    RawInline f $ substParams fm params $
                                    s ++ stringify' fm (map subst r)
                    where subst (Style "ALT") = RawInline f "$ALT$"
                          subst i = i
                Just _ -> i
        Just _ -> i
в
substInlineStyle' (Format fm) m style params i
    | Just (MetaMap mm) <- M.lookup style m
    , Just (MetaBlocks [Para ((RawInline f s):r)]) <- M.lookup fm mm =
        let subst (Style "ALT") = RawInline f "$ALT$"
            subst i = i
        in RawInline f $ substParams fm params $
                                s ++ stringify' fm (map subst r)
    | otherwise = i
Итак, мы заменили все case на охранные выражения, сократили код на 12 строк и сделали его более понятным. По-моему, это вин! Update. Реализовав локальную функцию substStyle’ в первом клозе substStyle с помощью охранных выражений, я, честно говоря, перестарался. В самом деле, какой смысл вытаскивать образец из связанной переменной, если эту переменную можно изначально представить в виде декомпозиции с интересующим нас конструктором? Использование охранных выражений с образцом в этом случае — это просто ненатуральный аналог сопоставления с образцом. Таким образом, первый клоз substStyle следует переписать так:
substStyle (Format fm) m b@(Para [Image ((Style style):alt) (src, title)])
    | Just (MetaMap mm) <- M.lookup style m
    , Just (MetaBlocks [mb]) <- M.lookup fm mm =
        let params = (alt, src, title)
            substStyle' (RawBlock f s) = RawBlock f $ substParams fm params s
            substStyle' b = walk substParams' b
                where substParams' (RawInline f s) =
                            RawInline f $ substParams fm params s
                      substParams' i = i
        in substStyle' mb
    | otherwise = b
И substParams’ теперь можно определить внутри блока where, поскольку два определения substStyle’ стали лексически независимы, в отличие от случая с охранными выражениями.

среда, 10 сентября 2014 г.

Ограничение мономорфным типом (monomorphism restriction) в haskell на конкретном примере

Дело было вечером, делать было нечего… Загрузил я ghci, чтобы немного развлечься.
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> let f = map (3*)
Prelude> :t f
f :: [Integer] -> [Integer]
Что за фигня, подумал я. Функция f не полиморфна! Но ведь это значит, что я не смогу ее применить, например, к списку вещественных чисел!
Prelude> let a = f [1.2, 3.4]

<interactive>:4:12:
    No instance for (Fractional Integer) arising from the literal `1.2'
    Possible fix: add an instance declaration for (Fractional Integer)
    In the expression: 1.2
    In the first argument of `f', namely `[1.2, 3.4]'
    In the expression: f [1.2, 3.4]
Так и есть. Попробуем с оригинальной полиморфной функцией map.
Prelude> :t map (3*)
map (3*) :: Num b => [b] -> [b]
Prelude> let b = map (3*) [1.2, 3.4]
Prelude> b
[3.5999999999999996,10.2]
Все работает, никаких неожиданностей. Неожиданностью остается обращение компилятора с функцией f. Давайте объявим аналогичную функцию f’ с явным указанием ожидаемого для нее полиморфного типа.
Prelude> :{
Prelude| let f' :: Num a => [a] -> [a]
Prelude|     f' = map (3*)
Prelude| :}
Prelude> let c = f' [1.2, 3.4]
Prelude> c
[3.5999999999999996,10.2]
Ух, сработало! Не все так плохо. Но почему же компилятор по умолчанию сужает исходный полиморфный тип функции map (3*) до конкретного [Integer] -> [Integer] в функции f? Как выяснило следствие, данный эффект обусловлен правилом ограничения мономорфным типом (monomorphism restriction), включенном в ghc по умолчанию. Главный довод для включения этого правила — обеспечение однократного вычисления значений внутри let и where, как этого обычно ожидает пользователь. Например, len в
f xs = (len, len)
       where len = genericLength xs
может быть вычислена более одного раза без включения ограничения мономорфным типом. Я взял данный пример из этой статьи — в ней объясняется почему такое может произойти. В Haskell Report проливается свет на условия применения ограничения мономорфным типом. В частности, Rule 1.(a) утверждает, что если мы свяжем все переменные в определении функции, то ее тип должен остаться полиморфным. Проверяем.
Prelude> let g x = map (3*) x
Prelude> :t g
g :: Num b => [b] -> [b]
Работает! А теперь удостоверимся, что исходное определение функции f при выключенном ограничении мономорфным типом станет полиморфным.
Prelude> :set -XNoMonomorphismRestriction 
Prelude> let f'' = map (3*)
Prelude> :t f''
f'' :: Num b => [b] -> [b]
Prelude> let d = f'' [1.2, 3.4]
Prelude> d
[3.5999999999999996,10.2]

суббота, 6 сентября 2014 г.

Синонимы образцов (pattern synonyms) в ghc-7.8 на конкретном примере

В предыдущей статье был представлен фильтр для pandoc, который позволяет настраивать стили изображений. В дальнейшем возможности фильтра были расширены и новая версия размещена на github. Основное нововведение — это возможность ссылаться на определение стиля, объявленного в метаданных документа, прямо из ссылки на изображение в исходном документе. Например, ссылка
![$img_style$ *Пример изображения*](../images/gnome-terminal-italic.png)
без использования фильтра styleFromMeta будет оформлена с альтернативным текстом img_style Пример изображения, причем запись img_style будет рассматриваться как встроенная математическая запись (inline math), поскольку она окружена символами доллара. Фильтр styleFromMeta проверяет, не начинается ли содержимое внутри квадратных скобок в определениях ссылок (и не только на изображения) со встроенной математической записи, и если это так, то пытается найти объект с именем этой записи в метаданных документа. В случае удачного поиска найденный объект подставляется в выходной документ с возможной заменой плейсхолдеров $ALT$, $SRC$ и $TITLE$ на значения из определения ссылки. Вместо $ALT$ подставляется остаток содержимого внутри квадратных скобок определения ссылки без начальных пробелов. В нашем примере значению $ALT$ будет соответствовать строка Пример изображения. Алгоритм поиска ссылок с возможным указанием на требуемый стиль реализуется с помощью сопоставления с образцом (pattern matching). Например, так.
substStyle :: Format -> MMap -> Block -> Block
substStyle (Format fm) m
           b@(Para [Image ((Math InlineMath style):alt) (src, title)]) =
Я не привел тело функции, поскольку к обсуждаемой проблеме оно не имеет отношения. Тип MMap объявлен как
type MMap = M.Map String MetaValue
, он соответствует способу хранения метаданных документа в pandoc. Главная проблема определения substStyle состоит в необходимости записывать образец для поиска стиля как Math InlineMath style. Но ведь это же просто деталь реализации! Просто так совпало, что мы выбрали обрамление долларами для отметки ссылок на стиль, и ссылки эти не имеют никакого отношения к встроенным математическим записям. Иными словами, использование оригинальных имен конструкторов в данном случае искажает семантику образца и не отражает наше намерение использовать его для извлечения ссылок на стиль. Определение
substStyle (Format fm) m b@(Para [Image ((Style style):alt) (src, title)]) =
выглядело бы гораздо лучше. Провернуть такой трюк позволит новое синтаксическое расширение Синонимы образцов (pattern synonyms), появившееся в ghc-7.8. Выражение Style в новом определении substStyle можно объявить как синоним образца.
pattern Style x <- Math InlineMath x
В данном случае мы объявили односторонний (unidirectional) синоним образца: для определения таких синонимов используется символ <-. Двухсторонние (bidirectional) синонимы объявляются с помощью символа =. Разница между двумя типами синонимов образцов простая: односторонние синонимы можно использовать только для сопоставления с образцом, в то время как двухсторонние — как обычные выражения. Подробнее о синонимах образцов можно узнать здесь. Все это хорошо, но что делать, если у нас в наличии старый компилятор ghc? Он не поймет, что это за объявление pattern Style x и что за Style объявлен в определении функции styleFromMeta. В нашем случае проблема легко решается введением условной компиляции и применением макросов cpp. В самом начале программы добавим объявления
{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 708
{-# LANGUAGE PatternSynonyms #-}
#endif
В первой строке заявлено, что мы хотим использовать препроцессор cpp. Далее, если версия ghc не древнее 7.8, мы включаем расширение PatternSynonyms. После объявлений импортов модулей вставляем строки
#if __GLASGOW_HASKELL__ >= 708
pattern Style x <- Math InlineMath x
#else
#define Style Math InlineMath
#endif
Если компилятор ghc древнее версии 7.8, то препроцессор cpp заменит слово Style на два слова Math InlineMath во всем исходном коде программы непосредственно перед ее компиляцией.