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

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

вычисление вложенных функций в императивных языках программирования.

В куче мы храним разные данные. Данные бывают статическими (они нужны нам на протяжении выполне-

ния всей программы) и динамическими (время жизни динамических данных заранее неизвестно, например

это могут быть отложенные вычисления, мы не знаем когда ни нам понадобятся). У кучи также две опера-

ции: выделить блок памяти, эта операция принимает размер блока и возвращает адрес, по которому удалось

выделить память, и освободить память по указанному адресу. Регистры находятся в процессоре. В них можно

записывать и читать данные, при этом операции обращения к регистрам будут происходить очень быстро.

Посмотрим как GHC справляется с переводом процесса редукции синонимов на язык понятный нашему

компьютеру. Язык обновления стека и кучи. Это большая и трудная глава, читайте не спеша. Если покажется

совсем трудно – пропустите, вернётесь потом, когда захочется писать не только красивые, но и эффективные

программы.

10.1 Этапы компиляции

Рассмотрим этапы компиляции программы (рис. 10.1).

На первых трёх этапах происходит проверка ошибок. Сначала мы строим синтаксическое дерево про-

граммы. Если мы нигде не забыли скобки, не ошиблись в простановке ключевых слов, то этот этап успешно

| 155

Файл .hs

Построение синтаксического дерева

Разрешение имён

Проверка типов

Устранение синтаксического сахара

Core

Упрощение Core

Генерация кода для ghci

STG

Генерация Cmm

C

Native

LLVM

Рис. 10.1: Этапы компиляции

завершится. Далее мы приписываем ко всем функциям их полные имена. Дописываем перед всеми опреде-

лениями имя модуля, в котором они определены. Обычно на этом этапе нам сообщают о том, что мы забыли

определить какую-нибудь функцию, часто это связано с простой опечаткой. Следующий этап – самый важ-

ный. Происходит вывод типов для всех значений и проверка программы по типам. Блок кода, отвечающий

за проверку типов, является самым большим в GHC. Haskell имеет очень развитую систему типов. Многих

возможностей мы ещё не коснулись, часть из них мы рассмотрим в главе 17. Допустим, что мы исправили

все ошибки связанные с типами, тогда компилятор начнёт переводить Haskell в Core.

Core – это функциональный язык программирования, который является сильно урезанной версией

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

ративный). Что хорошо для программиста, не очень хорошо для компилятора. Компилятор устраняет весь

синтаксический сахар и выражает все определения через простейшие конструкции языка Core. Далее проис-

ходит серия оптимизаций языка Core. На дереве описания программы выполняется серия функций типа Core

-> Core. Например происходит замена вызовов коротких функций на их правые части урвнений (встраивание

или inlining), выражения, которые проводят декомпозицию в case-выражениях по константам, заменяются

на соответствующие этим константам выражения. По требованию GHC может провести анализ строгости

(strictness analysis). Он заключается в том, что GHC ищет аргументы функций, которые могут быть вычисле-

ны более эфективно с помощью вычисления по значению и расставляет анотации строгости. И многие многие

другие оптимизации кода. Все они представлены в виде преобразования синтаксического дерева программы.

Также этот этап называют упрощением программы.

После этого Core переводится на STG. Это функциональный язык, повторяющий Core. Он содержит допол-