Средства разработки

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

Пакеты

В Haskell есть ещё один уровень организации данных, мы можем объединять модули в пакеты (package). Также как и модули пакеты могут зависеть от других пакетов, если они пользуются модулями этих пакетов. Одним пакетом мы уже пользовались и довольно часто, это пакет base, который содержит все стандартные модули, например такие как Prelude, Control.Applicative или Data.Function. Для создания и установки пакетов существует приложение cabal. Оно определяет протокол организации и распространения модулей Haskell.

Создание пакетов

Предположим, что мы написали программу, которая состоит из нескольких модулей. Пусть все модули хранятся в директории с именем src. Для того чтобы превратить набор модулей в пакет, нам необходимо поместить в одну директорию с src два файла:

.cabal

Посмотрим на простейший файл с описанием библиотеки, этот файл находится в одной директории с той директорией, в которой содержатся все модули приложения и имеет расширение .cabal:

Name        : Foo
Version     : 1.0

Library
  build-depends     : base
  exposed-modules   : Foo

Сначала идут свойства пакета. Общий формат определения свойства:

ИмяСвойства : Значение

В примере мы указали имя пакета Foo, и версию 1.0. После того, как мы указали все свойства, мы определяем будет наш пакет библиотекой или исполняемой программой или возможно он будет и тем и другим. Если пакет будет библиотекой, то мы помещаем за набором атрибутов слово Library, а если это исполняемая программа, то мы помещаем слово Executable, после мы пишем описание модулей пакета, зависимости от других пакетов, какие модули будут видны пользователю. Формат составления описаний в этой части такой же как и в самом начале файла. Сначала идёт зарезервированное слово-атрибут, затем через двоеточие следует значение. Обратите внимание на отступы за словом Library, они обязательны и сделаны с помощью пробелов, cabal не воспринимает табуляцию.

Файл .cabal может содержать комментарии, они делаются также как и в Haskell, закомментированная строка начинается с двойного тире.

Setup.hs

Файл Setup.hs содержит информацию о том как устанавливается библиотека. При установке могут использоваться другие программы и библиотеки. Пока мы будем пользоваться простейшим случаем:

import Distribution.Simple
main = defaultMain

Этот файл позволяет нам создавать библиотеки и приложения, которые созданы только с помощью Haskell. Это не так уж и мало!

Создаём библиотеки

Типичный файл .cabal для библиотеки выглядит так:

Name:           pinocchio
Version:        1.1.1
Cabal-Version:  >= 1.2
License:        BSD3
License-File:   LICENSE
Author:         Mister Geppetto
Homepage:       http://pinocchio.sourceforge.net/
Category:       AI
Synopsis:       Tools for creation of woodcrafted robots
Build-Type:     Simple


Library
  Build-Depends: base
  Hs-Source-Dirs: src/  
  Exposed-modules:
    Wood.Robot.Act, Wood.Robot.Percept, Wood.Robot.Think
  Other-Modules:
    Wood.Robot.Internals

Этим файлом мы описали библиотеку с именем pinocchio, версия 1.1.1, она использует версию cabal не ниже 1.2. Библиотека выпущена под лицензией BSD3. Файл с лицензией находится в текущей директории под именем LICENSE. Автор библиотеки Mister Geppetto. Подробнее узнать о библиотеке можно на её домашней странице http://pinocchio.sourceforge.net/. Атрибут Category указывает на широкую отрасль знаний, к которой принадлежит наша библиотека. В данном случае мы описываем библиотеку для построения роботов из дерева, об этом мы пишем в атрибуте Synopsis (краткое описание), поэтому наша библиотека принадлежит к категории искусственный интеллект или сокращённо AI. Последний атрибут Build-Type указывает на тип сборки пакета. Мы будем пользоваться значением Simple, который соответствует сборке с помощью простейшего файла Setup.hs, который мы рассмотрели в предыдущем разделе.

После описания пакета, идёт слово Library, ведь мы создаём библиотеку. Далее в атрибуте Build-Depends
мы указываем зависимости для нашего пакета. Здесь мы перечисляем все пакеты, которые мы используем в своей библиотеке. В данном случае мы пользовались лишь стандартной библиотекой base. В атрибуте hs-source-dirs мы указываем, где искать директорию с исходным кодом библиотеки. Затем мы указываем три внешних модуля, они будут доступны пользователю после установки библиотеки (атрибут Exposed-Modules), и внутренние скрытые модули (атрибут Other-Modules).

Создаём исполняемые программы

Типичный файл .cabal для исполняемой программы:

Name:           micro
Version:        0.0
Cabal-Version:  >= 1.2
License:        BSD3
Author:         Tony Reeds
Synopsis:       Small programming language
Build-Type:     Simple

Executable micro
  Build-Depends:  base, parsec
  Main-Is:        Main.hs
  Hs-Source-Dirs: micro

Executable micro-repl
  Main-Is:        Main.hs
  Build-Depends:  base, parsec
  Hs-Source-Dirs: repl
  Other-Modules:  Utils

В этом файле мы описываем две программы. Компилятор языка и интерпретатор языка micro. Если сравнить этот файл с файлом для библиотеки, то мы заметим лишь один новый атрибут. Это Main-Is. Он указывает в каком модуле содержится функция main. После установки этого пакета будут созданы два исполняемых файла. С именами micro и micro-repl.

Установка пакета

Пакеты устанавливаются с помощью команды install. Необходимо перейти в директорию пакета, ту, в которой находятся два служебных файла (.cabal и Setup.hs) и директория с исходниками, и запустить команду:

cabal install

Если мы нигде не ошиблись в описании пакета, не перепутали табуляцию с пробелами при отступах, или указали без ошибок все зависимости, то пакет успешно установится. Если это библиотека, то мы сможем подключать экспортируемые ей модули в любом другом модуле, просто указав их в директиве import. При этом нам уже не важно, где находятся модули библиотеки. Мы имеем возможность импортировать их из любого модуля. Если же пакет был исполняемой программой, будут созданы бинарные файлы программ. В конце cabal сообщит нам куда он их положил.

Иногда возникают проблемы с пакетами, которые генерируют исполняемые файлы, а затем с их помощью устанавливают другие пакеты. Проблема возникает из-за того, что cabal может положить бинарный файл в директорию, которая не видна следующим программам, которые хотят продолжить установку. В этом случае необходимо либо переложить созданные бинарные файлы в директорию, которая будет им видна, или добавить директорию с новыми бинарными файлами в PATH (под UNIX, Linux). Переменная операционной системы PATH содержит список всех путей, в которых система ищет исполняемые программы, если путь не указан явно. Посмотреть содержание PATH можно, вызвав:

$ echo $PATH

Появится строка директорий, которые записаны через двоеточие. Для того чтобы добавить директорию /data/dir в PATH необходимо написать:

$ PATH=$PATH:/data/dir

Эта команда добавит директорию в PATH для текущей сессии в терминале, если мы хотим записать её насовсем, мы добавим эту команду в специальный скрытый файл .bashrc, он находится в домашней директории пользователя. Под Windows добавить директорию в PATH можно с помощью графического интерфейса. Кликните правой кнопкой мыши на иконку My Computer (Мой Компьютер), в появившемся меню выберите вкладку Properties (Свойства). Появится окно System Properties (Свойства системы), в нём выберите вкладку Advanced и там нажмите на кнопку Environment variables (Переменные среды). И в этом окне будет строка Path, её мы и хотим отредактировать, добавив необходимые нам пути.

Давайте потренируемся и создадим библиотеку и исполняемую программу. Создадим библиотеку, которая выводит на экран Hello World. Создадим директорию hello, и в ней создадим директорию src. Эта директория будет содержать исходный код. Главный модуль библиотеки экспортирует функцию приветствия:

module Hello where

import Utility.Hello(hello)
import Utility.World(world)

helloWorld = hello ++ ", " ++ world ++ "!"

Главный модуль программы Main.hs определяет функцию main, которая выводит текст приветствия на экран:

module Main where

import Hello 

main = print helloWorld

У нас будет два внутренних модуля, каждый из которых определяет синоним для одного слова. Мы поместим их в папку Utility. Это модуль Utility.Hello

module Utility.Hello where
hello = "Hello"

И модуль Utility.World:

module Utility.World where
world = "World"

Исходники готовы, теперь приступим к описанию пакета. Создадим в корневой директории пакета файл hello.cabal.

Name:           hello
Version:        1.0
Cabal-Version:  >= 1.2
License:        BSD3
Author:         Anton
Synopsis:       Little example of cabal usage
Category:       Example
Build-Type:     Simple

Library
  Build-Depends: base == 4.*
  Hs-Source-Dirs: src/
  Exposed-modules:
    Hello
  Other-Modules:
    Utility.Hello
    Utility.World

Executable hello
  Build-Depends: base == 4.*
  Main-Is: Main.hs
  Hs-Source-Dirs: src/

В этом файле мы описали библиотеку и программу. В строке base == 4.* мы указали версию пакета base. Запись 4.* означает любая версия, которая начинается с четвёрки. Осталось только поместить в корневую директорию пакета файл Setup.hs.

import Distribution.Simple
main = defaultMain

Теперь мы можем переключиться на корневую директорию пакета и установить пакет:

anton@anton-desktop:~/haskell-notes/code/ch-17/hello$ cabal install
Resolving dependencies...
Configuring hello-1.0...
Preprocessing library hello-1.0...
Preprocessing executables for hello-1.0...
Building hello-1.0...
[1 of 3] Compiling Utility.World    ( src/Utility/World.hs, dist/build/Utility/World.o )
[2 of 3] Compiling Utility.Hello    ( src/Utility/Hello.hs, dist/build/Utility/Hello.o )
[3 of 3] Compiling Hello            ( src/Hello.hs, dist/build/Hello.o )
Registering hello-1.0...
[1 of 4] Compiling Utility.World    ( src/Utility/World.hs, dist/build/hello/hello-tmp/Utility/World.o )
[2 of 4] Compiling Utility.Hello    ( src/Utility/Hello.hs, dist/build/hello/hello-tmp/Utility/Hello.o )
[3 of 4] Compiling Hello            ( src/Hello.hs, dist/build/hello/hello-tmp/Hello.o )
[4 of 4] Compiling Main             ( src/Main.hs, dist/build/hello/hello-tmp/Main.o )
Linking dist/build/hello/hello ...
Installing library in /home/anton/.cabal/lib/hello-1.0/ghc-7.4.1
Installing executable(s) in /home/anton/.cabal/bin
Registering hello-1.0...

Мы видим сообщения о процессе установки. После установки в текущей директории пакета появилась директория dist, в которую были помещены скомпилированные файлы библиотеки. В последних строках cabal сообщил нам о том, что он установил библиотеку в директорию:

Installing library in /home/anton/.cabal/lib/hello-1.0/ghc-7.4.1

и исполняемый файл в директорию:

Installing executable(s) in /home/anton/.cabal/bin

С помощью различных флагов мы можем контролировать процесс установки пакета. Назначать дополнительные директории, указывать куда поместить скомпилированные файлы. Подробно об этом можно почитать в справке, выполнив в командной строке одну из команд:

cabal --help
cabal install --help

Если у вас не получилось сразу установить пакет не отчаивайтесь и почитайте сообщения об ошибках из cabal, он информативно жалуется о забытых зависимостях и неспособности правильно прочитать файл с описанием пакета.

Удаление библиотеки

Установленные с помощью cabal файлы видны из любого модуля. Имена модулей регистрируются глобально. Если нам захочется установить библиотеку с уже зарегистрированным именем, произойдёт хаос. Возможно прежняя библиотека нам уже не нужна. Как нам удалить её? Посмотрим на решение для компилятора ghc. Мы можем посмотреть список всех зарегистрированных в ghc библиотек с помощью команды:

$ ghc-pkg list
   Cabal-1.8.0.6
   array-0.3.0.1
   base-4.2.0.2
   ...
   ...

Появится длинный список с именами библиотек. Для удаления одной из них мы можем выполнить команду:

ghc-pkg unregister имя-библиотеки

Например так мы можем удалить только что установленную библиотеку hello:

$ ghc-pkg unregister hello

Репозиторий пакетов Hackage

Если у нас подключен интернет, то мы можем воспользоваться наследием сообщества Haskell и установить пакет с Hackage. Там расположено много-много-много пакетов. Любой разработчик Haskell может добавить свой пакет на Hackage. Посмотреть на пакеты можно на сайте этого репозитория:

http://hackage.haskell.org

Если для вашей задачи необходимо выполнить какую-нибудь довольно общую задачу, например написать тип красно-чёрных деревьев или построить парсер или возможно вам нужен веб-сервер, поищите этот пакет на Hackage, он там наверняка окажется, ещё и в нескольких вариантах.

Для установки пакета с Hackage нужно просто написать

cabal install имя-пакета

Возможно нам нужен очень новый пакет, который был только что залит автором на Hackage. Тогда выполняем:

cabal update

Происходит обновление данных о загруженных на Hackage. Что хорошо, вы можете загрузить исходники из Hackage, например у вас никак не получается написать пакет, который устанавливался бы без ошибок. Просто загрузим исходники какого-нибудь пакета из Hackage и посмотрим на пример рабочего пакета.

Дополнительные атрибуты пакета

В файле .cabal также часто указывают такие атрибуты как:

Maintainer

Поле содержит адрес электронной почты технической поддержки. Это те люди, к которым посыпятся сообщения об ошибках или запросы на новые возможности.

Stability

Статус версии библиотеки (provisional, experimental, unstable).

Description

Подробное описание назначения пакета. Оно помещается на главную страницу пакета в документации.

Extra-Source-Files

В этом поле можно через пробел указать дополнительные файлы, включаемые в пакет. Это могут быть примеры использования, описание в формате PDF или хроника изменений и другие служебные файлы. Все эти файлы будут включены в архив проекта.

License-file

Путь к файлу с лицензией.

ghc-options

Флаги компиляции для GHC. Если в нашей библиотеке мы активно пользуемся продвинутыми прагмами оптимизации, необходимо сообщить об этом компилятору пользователя. Например, мы можем написать в этом атрибуте -O или -O2.

Установка библиотек для профилирования

Помните когда-то мы занимались профилированием? Это было в главе, посвящённой устройству GHC. Мы включали флаг -prof и всё шло гладко. Там мы профилировали код, в котором участвовали лишь стандартные библиотеки из пакета base, такие как Prelude. Но если мы попробуем профилировать код с какими-нибудь другими библиотеками, установленными с помощью cabal, GHC возмутится и скажет, что для профилирования не хватает специальной версии библиотеки имярек. Для того чтобы иметь возможность профилировать код, в котором участвуют другие библиотеки необходимо установить их с возможностью профилирования. Это делается при установке с помощью специального флага --``enable-library-profiling или --``enable-executable-profiling (если мы устанавливаем исполняемое приложение):

$ cabal install имярек --reinstall --enable-library-profiling

Библиотека будет установлена в двух экземплярах: для исполнения и профилирования. Возможно библиотека имярек потребует переустановки некоторых библиотек, от которых она зависит. Повторяем эту процедуру для этих библиотек и возвращаемся к исходной библиотеке. К сожалению, избежать переустановки библиотек нельзя. Но мы можем сделать так, чтобы все будущие библиотеки устанавливались с возможностью профилирования. Для этого необходимо отредактировать файл настроек программы cabal. Ищем директорию, в которой cabal хранит свои служебные файлы. Если вы пользуетесь Linux, то скорее всего это скрытая директория .cabal в вашей домашней директории. Если вы пользуетесь Windows, положение директории зависит от версии системы. Но ничего, узнать её положение можно, выполнив в ghci

Prelude> :m System.Directory
Prelude System.Directory> getAppUserDataDirectory "cabal"

Присмотритесь к этой директории в ней вы найдёте много полезных данных. В ней находятся исполняемые программы, скомпилированные библиотеки, а также исходный код библиотек. В этой директории находится и файл config с настройками для cabal. Ищем строчку с полем library-profiling: False. Меняем значение на True и раскомментируем эту строчку, если она закомментирована. После этого cabal install будет устанавливать библиотеки для профилирования. На первых порах это вызовет массу неудобств из-за необходимости переустановки многих библиотек.

Если пакет не устанавливается

Нам нужен пакет с Hackage, но мы никак не можем его установить. Мы видим документацию на странице проекта. Мы понимаем, что он собрался на Hackage, но покаким-то причинам он никак не может установиться на нашем компьютере. Рассмотрим типичные загвоздки:

Отметим, что очень часто пакет не может установиться из-за какого-то совсем другого пакета, от которого он зависит. И в этом случае мы не отчаиваемся включаем хакерское настроение и читаем дальше!

Такая ошибка возникает очень часто при установке библиотек, которые занимаются разбором (parsing) синтаксиса каких-нибудь языков. Например установка бибилотеки haskell-src-exts может закончиться с ошибкой, вроде:

`alex` not found 

или

`happy` not found 

alex – это программа для создания лексеров на Haskel, а happy – это программа для создания парсеров. Смысл ошибки в том, что у нас не установленна Haskell-программа. Скорее всего она есть на Hackage. Хорошо, установим такую программу, например:

$ cabal install alex

И убедимся в том, что директория, в которую cabal устанавливает программы внесена в PATH. Обычно это директория .cabal/bin. Теперь продолжим установку библиотеки:

$ cabal install haskell-src-exts

В Haskell есть возможность использовать Си-библиотеки через FFI (Foreign Function Interface). Например, мы можем вызывать функции библиотеки OpenGL из Haskell. Но пакет, который вызывает функции Си-библиотеки, установится успешно, только если Си-библиотека будет установлена. Перед установкой пакет проверяет наличие заголовочных файлов (h-файлы в Си). И в этот момент установка может оборваться с сообщением, вроде

fuzzybuzzy.h not found

Необходимо установить Си-библиотеку fuzzybuzzy вместе с заголовочными файлами перед установкой пакета. В Debian/Ubuntu эта проблема очень часто решается с помощью:

$ sudo apt-get install libfuzzybuzzy-dev

Часто имя Си-библиотек похоже на libИмяБибилотеки-dev. Или можно поискать такую библиотеку с помощью:

$ apt-cache search fuzzybuzzy

Пакет зависит от mtl версии 2.0.1, но у нас уже установлен пакет mtl-3.0 и есть другие пакеты, которые так нужны и не могут зависеть от mtl-2.0.1. Как быть? Эту проблему пока невозможно решить с помощью cabal. Но есть программа cabal-dev. Обычный cabal устанавливает все пакеты в одно глобальное пространство пакетов. cabal-dev позволяет создавать локальные пространства пакетов или “песочницы” (sandbox). Там будут только пакеты, которые нужны только для одного проекта. Причём ни один из пакетов не будет виден глобально. Сначала установим сам cabal-dev:

$ cabal install cabal-dev

Пусть у нас есть проект с cabal-файлом. Переключимся в директорию проекта и выполним:

$ cabal-dev install

Проект установится, причём все пакеты, откоторых он зависит будут установлены в созданную в проекте директорию cabal-dev. Для первой установки может потребоваться много времени. Также мы можем указать директорию для пакетов песочницы явно:

$ cabal-dev install --sandbox=/usr/path/to/local/packages

Мы можем устанавливать в песочницу и пакеты с Hackadge. Для этого просто пишем cabal-dev вместо cabal при установке и указываем в какую песочницу установить пакет. Только недавно так устанавливал fay – компилятор для создания javascript-кода из Haskell-кода. Для запуска интерпретатора в песочнице наберём:

$ cabal-dev ghci

cabal пока так не умеет. Но как раз сейчас запущен проект для того, чтобы научить его этому.

Выходят новые версии компилятора ghc, но автор такого нужного нам пакета увлёкся какой-то совсем другой задачей и забыл про этот такой нужный нам пакет. Не беда! Мы можем написать автору. Или, если нам необходимо срочное решение, скачать исходный код пакета и поправить зависимости от base и ghc-prim или других библиотек. Вдруг повезёт и всё установится? Для установки мы переключимся на директорию пакета и установим его локально через cabal install.

Например, пакет может пользоваться библиотеками, которые есть только в одной из операционных систем, но не в нашей. Такие дела, остаётся только поменять ОС или поискать другой пакет.

Создание документации с помощью Haddock

Если мы зайдём на Hackage, то там мы увидим длинный список пакетов, отсортированных по категориям. К какой категории какой пакет относится мы указываем в .cabal-файле в атрибуте Category. Далее рядом с именем пакета мы видим краткое описание, которое берётся из атрибута Synopsis. Если мы зайдём на страницу одного из пакетов, то там мы увидим страницу в таком же формате, что и документация к стандартным библиотекам. Мы видим описание пакета и ниже иерархию модулей. Мы можем зайти в заинтересовавший нас модуль и посмотреть на объявленные функции, типы и классы. В самом низу страницы находится ссылка к исходникам пакета.

“Домашняя страница” пакета была создана с помощью приложения Haddock. Оно генерирует документацию в формате html по специальным комментариям. Haddock встроен в cabal, например, мы можем сделать документацию к нашему пакету hello. Для этого нужно переключиться на корневую директорию пакета и вызвать:

cabal haddock

После этого в директории dist появится директория doc, в которой внутри директории html находится созданная документация. Мы можем открыть файл index.html, и там мы увидим “иерархию нашего” модуля. В модуле пока нет ни одной функции: так получилось потому, что Haddock помещает в документацию лишь те функции, у которых есть объявление типа. Если мы добавим в модуле Hello.hs к единственной функции объявление типа:

helloWorld :: String
helloWorld = hello ++ ", " ++ world ++ "!"

И теперь перезапустим haddock, то мы увидим, что в модуле Hello появилась одна запись.

Комментарии к определениям

Прокомментировать любое определение можно с помощью комментария следующего вида:

-- | Here is the comment
helloWorld :: String
helloWorld = hello ++ ", " ++ world ++ "!"

Обратите внимание на значок “или” сразу после комментариев. Этот комментарий будет включен в документацию. Также можно писать комментарии после определения: для этого к комментарию добавляется значок степени:

helloWorld :: String
helloWorld = hello ++ ", " ++ world ++ "!"
-- ^ Here is the comment

К сожалению, на момент написания этих строк, Haddock может включать в документацию лишь латинские символы. Комментарии могут простираться на несколько строк:

-- | Here is the type.
-- It contains three elements.
-- That's it.
data T = A | B | C

Также они могут быть блочными:

{-|
   Here is the type.
   It contains three elements.
   That's it.
 -}
data T = A | B | C

Мы можем комментировать не только определение целиком, но и отдельные части. Например, так мы можем пояснить отдельные аргументы у функции:

add :: Num a => a   -- ^ The first argument
             -> a   -- ^ The second argument   
             -> a   -- ^ The return value   

Методы класса и отдельные конструкторы типа можно комментировать как обычные функции:

data T
        -- | constructor A    
       = A      
        -- | constructor B
       | B      
        -- | constructor C
       | C     

Или так:

data T = A      -- ^ constructor A
       | B      -- ^ constructor B
       | C      -- ^ and so on

Комментарии к классу:

-- | С-class
class С a where
    -- | f-function
    f :: a -> a
    -- | g-function
    g :: a -> a

Комментарии к модулю

Комментарии к модулю помещаются перед объявлением имени модуля. Эта информация попадёт в самое начало страницы документации:

-- | Little example
module Hello where

Структура страницы документации

Если модуль большой, то его бывает удобно разделить на части, словно разделы в главе книги. Определения группируются по функциональности и помещаются в разные разделы или даже подразделы. Структура документации определяется с помощью специальных комментариев в экспорте модуля. Посмотрим на пример:

-- | Little example
module Hello(
    -- * Introduction
    -- | Here is the little example to show you
    -- how to make docs with Haddock
    
    -- * Types
    -- | The types.
    T(..),
    -- * Classes
    -- | The classes.
    C(..),
    -- * Functions
    helloWorld
    -- ** Subfunctions1
    -- ** Subfunctions2    
) where

...

Комментарии со звёздочкой создают раздел, а с двумя звёздочками – подраздел. Те определения, которые экспортируются за комментариями со звёздочкой попадут в один раздел или подраздел. Если сразу за комментарием со звёздочкой идёт комментарий со знаком “или”, то он будет помещён в самое начало раздела. В нём мы можем пояснить по какому принципу группируются определения в данном разделе.

Разметка

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

При этом символы \, ', `, ", @, < являются специальными. Если вы хотите воспользоваться одним из специальных символов в тексте, необходимо написать перед ним обратный слэш \. Также символы для обозначения комментариев *, |, ^ и > являются специальными, если они расположены в самом начале строки.

Параграфы

Параграфы определяются по пустой строке в комментарии. Так, например, мы можем разбить текст на два параграфа:

-- | The first paragraph goes here.
--
-- The second paragraph goes here.
fun :: a -> b

Блоки кода

Существует два способа обозначения блоков кода:

-- | This documentation includes two blocks of code:
--
-- @
--     f x = x + x
--     g x = x
-- @
--
-- >  g x = x * 42

В первом варианте мы заключаем блок кода в окружение @...@. Так мы можем выделить целый кусок кода. Для выделения одной строки мы можем воспользоваться знаком >.

Примеры вычисления в интерпретаторе

В Haddock мы можем привести пример вычисления выражения в интерпретаторе. Это делается с помощью тройного символа >:

-- | Two examples are given bellow:
--
-- >>> 2+3
-- 5
--
-- >>> print 1 >> print 2
-- 1
-- 2

Строки, которые идут сразу за строкой с символом >>>, помечаются как результат выполнения выражения в интерпретаторе.

Имена определений

Для того чтобы выделить имя любого определения, будь то функция, тип или класс, необходимо заключить его в ординарные кавычки, как в 'T'. При этом Haddock установит ссылку к определению и подсветит имя в тексте. Для того чтобы сослаться на определение из другого модуля, необходимо написать его полное имя, то есть с приставкой имени модуля, например, функция fun, определённая в модуле M, имеет полное имя M.fun – тогда в комментариях мы обозначаем её 'M.fun'.

Ординарные кавычки часто используются в английском языке как апострофы, в таких сочетаниях как don’t, isn’t. Перед такими вхождениями ординарных кавычек можно не писать обратный слэш – Haddock сумеет отличить их от идентификатора.

Курсив и моноширинный шрифт

Для выделения текста курсивом он заключается в окружение .... Для написания текста моноширинным шрифтом он заключается в окружение @...@.

Модули

Для обозначения модулей используются двойные кавычки, как в

-- | This is a reference to the "Foo" module.

Списки

Список без нумерации обозначается с помощью звёздочек:

-- | This is a bulleted list:
--
--     * first item
--
--     * second item

Пронумерованный список, обозначается символами (n) или n. (n с точкой), где n – некоторое целое число:

-- | This is an enumerated list:
--
--     (1) first item
--
--     2. second item

Список определений

Определения обозначаются квадратными скобками, например, комментарий:

-- | This is a definition list:
--
--   [@foo@] The description of @foo@.
--
--   [@bar@] The description of @bar@.

в документации будет выглядеть так:

foo

The description of foo.

bar

The description of bar.

Для выделения текста моноширинным шрифтом мы воспользовались окружением @...@.

URL

Ссылки на сайты включаются с помощью окружения <...>.

Ссылки внутри модуля

Для того чтобы сослаться на какой-нибудь текст внутри модуля, его необходимо отметить ссылкой. Для этого мы помещаем в том месте, на которое мы хотим сослаться, запись #label#, где label – это идентификатор ссылки. Теперь мы можем сослаться на это место из другого модуля с помощью записи "module#label", где module – имя модуля, в котором находится ссылка label.

Краткое содержание

В этой главе мы познакомились с основными элементами арсенала разработчика программ. Мы научились создавать библиотеки и документировать их.

Упражнения

Вспомните один из примеров и превратите его в библиотеку. Например, напишите библиотеку для натуральных чисел Пеано.

Зарегистрировано под лицензией Creative commons Attribution-NonCommercial-NoDerivs 3.0 Generic (CC BY-NC-ND 3.0)