Данный документ описывает рекомендуемые способы по работе с Haskell в плане сборки проектов.
Программы на языке Haskell пишутся в файлах с расширением .hs
.
На самом деле есть ещё
.lhs
, но в рамках курса мы эту тему затрагивать не будем.
Haskell является компилируемым языком. Компилятором Haskell (основным и, можно считать, единственным) является GHC. Предполагается, что решения домашних заданий будут разделены на несколько файлов (называемых модулями) и будут использовать сторонние библиотеки. Поэтому намного удобней организовать решения в проекты. А чтобы работать с проектами, необходимо использовать билд-тулу (программу, занимающеся компиляцией целого проекта, состоящего из нескольких модулей и используещего различные библиотеки).
На данный момент существует несколько сборщиков проектов:
stack
cabal-install
(далее просто cabal
)
Ниже будут описансы способы по установке инструментов компиляции проектов.
stack
(рекомендуемый)Рекомендации по установке stack
находятся по этой ссылке:
Там есть инструкции для каждой системы.
С помощью команды stack new project
можно создать проект с названием project
.
После этого удалите файл package.yaml
из проекта, если такой файл создался
Скомпилировать проект: stack build
Запустить ghci из проекта: stack ghci
Требуется использовать следующую структуру проекта:
В README
этого репозитория есть команда, которая создает требуемую структуру.
То есть структура директорий будет выглядеть примерно следующим образом:
├── hw1
│ ├── hw1.cabal
│ └── src
│ └── Task1.hs
│ └── Task2.hs
├── hw2
│ ├── hw2.cabal
│ ├── app
│ │ └── Main.hs
│ └── src
│ └── Task1.hs
│ └── Task2.hs
│ └── Task3.hs
├── hw3
│ ├── hw3.cabal
│ ├── app
│ │ └── Main.hs
│ ├── src
│ └── Task1.hs
│ └── test
│ └── Spec.hs
└── stack.yaml
Каждому домашнему заданию отведен отдельный пакет (hw1
, hw2
, hw3
, etc). stack.yaml
общий для всех пакетов (домашних заданий). Если Вам потребовалось создать executable для пакета, он должен находиться в поддиректории app/
, тесты –- в test/
.
Чтобы собрать пакет hw1
используйте команду: stack build hw1
Чтобы запустить тесты пакета hw1
используйте команду stack test hw1
Чтобы запустить ghci
в рамках пакета hw1
используйте команду
stack ghci hw1
cabal
(для общего развития)NOTE: инструкции ниже для Ubuntu. Надеюсь, пользователи других дистрибутивов смогут разобраться.
Пакеты GHC и cabal
поддерживается в PPA репозитории для Ubuntu:
Надо просто набрать:
sudo add-apt-repository ppa:hvr/ghc
sudo apt update
cabal
Последняя вышедшая версия компилятора GHC: 8.2.2. Во время курса будем использовать её. cabal-install
надо ставить cамой последней версии – HEAD
.
sudo apt install ghc-8.2.2 cabal-install-head
Можно проверить, что используются последние версии (версия cabal
на момент прохождения инструкции может быть новее).
$ /opt/ghc/8.2.2/bin/ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.2.2
$ /opt/cabal/head/bin/cabal --version
cabal-install version 2.1.0.0
compiled using version 2.1.0.0 of the Cabal library
cabal
необходимо добавить в переменную окружения $PATH
. Это можно сделать следующим образом:
$ echo 'export PATH="$PATH:/opt/cabal/head/bin"' >> ~/.profile
$ . ~/.profile
Тоже самое рекомендуется сделать для ghc
.
Создайте приватный репозиторий на GitHub. В репозитории создайте папку hw1
. Далее перейдите в эту папку и запустите команду cabal init
. Эта команда позволяет создать проект в интерактивном режиме. На этом шаге должно быть всё более-менее понятно. После результата выполнения этой команды из папки hw1
содержимое папки должно выглядеть следующим образом:
$ tree .
.
├── ChangeLog.md
├── LICENSE
├── Setup.hs
├── src
└── hw1.cabal
1 directory, 4 files
Создайте в папочке src
файл с названием Dummy.hs
и поместите в него следующий код:
module Dummy where
inc :: Int -> Int
inc x = x + 1
После этого отредактируйте hw1.cabal
файл, добавив в него новый модуль. Для этого надо заменить:
-- exposed-modules:
на
exposed-modules: Dummy
NOTE: все модули проекта должны быть перечислены в
exposed-modules
, чтобы они компилировались.
Сначала из проекта надо запустить команду cabal new-update
. Эту команду нужно запускать каждый раз, когда вы удаляете папочку ~/.cabal
.
А обычно, если что-то не билдится или идёт не так, то удаление этой папочки решает проблемы.
После этого надо набрать команду cabal new-build
, чтобы скомпилировать проект.
Важно! В
cabal
есть командыbuild, update
и прочие. Необходимо использовать команды только с префиксомnew-
. Команды с этим префиксом совсем другие и они работают хорошо.
Вы должны увидеть, что файл Dummy
скомпилировался.
После этого можно запустить ghci
, и вызвать оттуда функцию inc
, чтобы убедиться в её работе!
Итого:
$ cabal new-update
$ cabal new-build
$ cabal new-repl
...
Prelude> inc 4
5
Если хочется поиграться только с ghci, то шаг с
new-build
можно опускать. Он необходим, только если Вы будете создавать исполняемые файлы.
В мире Haskell, к сожалению, очень давно не утихают споры, какой инструмент сборки проектов лучше. Вместо субъективных суждений, данный документ просто опишет некоторые особенности.
Термин cabal
неоднозначный. В мире Haskell под ним может подразумеваться разное в зависимости от контекста:
cabal
как cabal-install
– инструмент сборки проектов.cabal
как формат конфигурации проектов на Haskell с расширением .cabal
. Вся конфигурация проекта (все модули проекта, зависимости проекта, метаданные) находятся в файле project-name.cabal
и описана в специальном синтаксисе. Инструменты сборки (оба cabal
и stack
) используют информацию из этого файла для сборки проекта.cabal
как библиотека Cabal
, которая парсит файлы в формате .cabal
.В этом документе и на лекциях под cabal
неявно подразумевается cabal-install
, а в остальных случаях идёт явное уточнение.
Все библиотеки, которые нужны вашему проекту, надо перечислять в поле build-depends
. По умолчанию Ваш проект использует библиотеку base
– стандартную библиотеку. Но также есть, например, библиотека containers
, содержащая типы данных Map
, Set
(реализованные на сбалансированных двоичных деревьев).
Если Вы используете cabal
, то необходимо указывать нижнюю и верхнюю границу версий библиотек, с которыми Вам известно, что проект точно собирается.
Если Вы используете stack
, то границы в .cabal
файле указывать нет необходимости. stack
использует информацию из stack.yaml
. В этом файле указан resolver
, который используется для сборки вашего проекта (например, lts-10.5
). Resolver – по сути снимок версий пакетов с Hackage. В вашем проекте будут использоваться только те версии, которые указаны в резолвере. Их можно найти здесь, например:
В первом домашнем задании не потребуются библиотеки кроме
base
, поэтому пока можно сильно не задумываться о зависимостях.