В этой главе мы познакомимся с основными средствами разработки больших программ. Мы научимся устанавливать и создавать библиотеки, писать документацию.
В Haskell есть ещё один уровень организации данных, мы можем объединять модули в пакеты (package). Также как и модули пакеты могут зависеть от других пакетов, если они пользуются модулями этих пакетов. Одним пакетом мы уже пользовались и довольно часто, это пакет base
, который содержит все стандартные модули, например такие как Prelude
, Control.Applicative
или Data.Function
. Для создания и установки пакетов существует приложение cabal
. Оно определяет протокол организации и распространения модулей Haskell.
Предположим, что мы написали программу, которая состоит из нескольких модулей. Пусть все модули хранятся в директории с именем src
. Для того чтобы превратить набор модулей в пакет, нам необходимо поместить в одну директорию с src
два файла:
имяПакета.cabal
– файл с описанием пакета.
Setup.hs
– файл с инструкциями по установке пакета
Посмотрим на простейший файл с описанием библиотеки, этот файл находится в одной директории с той директорией, в которой содержатся все модули приложения и имеет расширение .cabal
:
Name : Foo Version : 1.0 Library build-depends : base exposed-modules : Foo
Сначала идут свойства пакета. Общий формат определения свойства:
ИмяСвойства : Значение
В примере мы указали имя пакета Foo
, и версию 1.0
. После того, как мы указали все свойства, мы определяем будет наш пакет библиотекой или исполняемой программой или возможно он будет и тем и другим. Если пакет будет библиотекой, то мы помещаем за набором атрибутов слово Library
, а если это исполняемая программа, то мы помещаем слово Executable
, после мы пишем описание модулей пакета, зависимости от других пакетов, какие модули будут видны пользователю. Формат составления описаний в этой части такой же как и в самом начале файла. Сначала идёт зарезервированное слово-атрибут, затем через двоеточие следует значение. Обратите внимание на отступы за словом Library
, они обязательны и сделаны с помощью пробелов, cabal
не воспринимает табуляцию.
Файл .cabal
может содержать комментарии, они делаются также как и в Haskell, закомментированная строка начинается с двойного тире.
Файл 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
Если у нас подключен интернет, то мы можем воспользоваться наследием сообщества Haskell и установить пакет с Hackage
. Там расположено много-много-много пакетов. Любой разработчик Haskell может добавить свой пакет на Hackage
. Посмотреть на пакеты можно на сайте этого репозитория:
Если для вашей задачи необходимо выполнить какую-нибудь довольно общую задачу, например написать тип красно-чёрных деревьев или построить парсер или возможно вам нужен веб-сервер, поищите этот пакет на 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
пока так не умеет. Но как раз сейчас запущен проект для того, чтобы научить его этому.
base
или ghc-prim
Выходят новые версии компилятора ghc, но автор такого нужного нам пакета увлёкся какой-то совсем другой задачей и забыл про этот такой нужный нам пакет. Не беда! Мы можем написать автору. Или, если нам необходимо срочное решение, скачать исходный код пакета и поправить зависимости от base
и ghc-prim
или других библиотек. Вдруг повезёт и всё установится? Для установки мы переключимся на директорию пакета и установим его локально через cabal install
.
Например, пакет может пользоваться библиотеками, которые есть только в одной из операционных систем, но не в нашей. Такие дела, остаётся только поменять ОС или поискать другой пакет.
Если мы зайдём на 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
.
Для выделения текста моноширинным шрифтом мы воспользовались окружением @...@
.
Ссылки на сайты включаются с помощью окружения <...>
.
Для того чтобы сослаться на какой-нибудь текст внутри модуля, его необходимо отметить ссылкой. Для этого мы помещаем в том месте, на которое мы хотим сослаться, запись #label#
, где label
– это идентификатор ссылки. Теперь мы можем сослаться на это место из другого модуля с помощью записи "module#label"
, где module
– имя модуля, в котором находится ссылка label
.
В этой главе мы познакомились с основными элементами арсенала разработчика программ. Мы научились создавать библиотеки и документировать их.
Вспомните один из примеров и превратите его в библиотеку. Например, напишите библиотеку для натуральных чисел Пеано.