# Haskell: Build tools (настраиваем окружение)
Данный документ описывает рекомендуемые способы по работе с Haskell в плане сборки проектов.
## Build tools (установка)
Программы на языке Haskell пишутся в файлах с расширением `.hs`.
> На самом деле есть ещё `.lhs`, но в рамках курса мы эту тему затрагивать не будем.
Haskell является компилируемым языком. Компилятором Haskell (основным и, можно считать, единственным) является [GHC](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/). Предполагается, что решения домашних заданий будут разделены на несколько файлов (называемых _модулями_) и будут использовать сторонние библиотеки. Поэтому намного удобней организовать решения в _проекты_. А чтобы работать с проектами, необходимо использовать билд-тулу (программу, занимающеся компиляцией целого проекта, состоящего из нескольких модулей и используещего различные библиотеки).
На данный момент существует несколько сборщиков проектов:
1. `stack`
* https://docs.haskellstack.org/en/stable/README/
1. `cabal-install` (далее просто `cabal`)
* https://www.haskell.org/cabal/users-guide/index.html
Ниже будут описансы способы по установке инструментов компиляции проектов.
### `stack` (рекомендуемый)
Рекомендации по установке `stack` находятся по этой ссылке:
* https://docs.haskellstack.org/en/stable/README/#how-to-install
Там есть инструкции для каждой системы.
С помощью команды `stack new project` можно создать проект с названием `project`.
После этого удалите файл `package.yaml` из проекта, если такой файл создался
Скомпилировать проект: `stack build`
Запустить ghci из проекта: `stack ghci`
#### Шаблон проектов в ДЗ
Требуется использовать следующую структуру проекта:
* https://github.com/pva701/fp-homework-templates
В `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. Надеюсь, пользователи других дистрибутивов смогут разобраться.
#### Шаг 1: Добавить PPA репозиторий
Пакеты GHC и `cabal` поддерживается в PPA репозитории для Ubuntu:
* https://launchpad.net/~hvr/+archive/ubuntu/ghc
Надо просто набрать:
```
sudo add-apt-repository ppa:hvr/ghc
sudo apt update
```
#### Шаг 2: Установка GHC и `cabal`
Последняя вышедшая версия компилятора GHC: 8.2.2. Во время курса будем использовать её. `cabal-install` надо ставить cамой последней версии -- `HEAD`.
```
sudo apt install ghc-8.2.2 cabal-install-head
```
Можно проверить, что используются последние версии (версия `cabal` на момент прохождения инструкции может быть новее).
```shell
$ /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`. Это можно сделать следующим образом:
```shell
$ echo 'export PATH="$PATH:/opt/cabal/head/bin"' >> ~/.profile
$ . ~/.profile
```
Тоже самое рекомендуется сделать для `ghc`.
#### Шаг 3: Создание первого проекта
Создайте приватный репозиторий на GitHub. В репозитории создайте папку `hw1`. Далее перейдите в эту папку и запустите команду `cabal init`. Эта команда позволяет создать проект в интерактивном режиме. На этом шаге должно быть всё более-менее понятно. После результата выполнения этой команды из папки `hw1` содержимое папки должно выглядеть следующим образом:
```shell=
$ tree .
.
├── ChangeLog.md
├── LICENSE
├── Setup.hs
├── src
└── hw1.cabal
1 directory, 4 files
```
#### Шаг 4: Создание первого модуля
Создайте в папочке `src` файл с названием `Dummy.hs` и поместите в него следующий код:
```haskell=
module Dummy where
inc :: Int -> Int
inc x = x + 1
```
После этого отредактируйте `hw1.cabal` файл, добавив в него новый модуль. Для этого надо заменить:
```
-- exposed-modules:
```
на
```
exposed-modules: Dummy
```
> **NOTE:** все модули проекта должны быть перечислены в `exposed-modules`, чтобы они компилировались.
#### Шаг 5: Сборка проекта
Сначала из проекта надо запустить команду `cabal new-update`. Эту команду нужно запускать каждый раз, когда вы удаляете папочку `~/.cabal`.
> А обычно, если что-то не билдится или идёт не так, то удаление этой папочки решает проблемы.
После этого надо набрать команду `cabal new-build`, чтобы скомпилировать проект.
> **Важно!** В `cabal` есть команды `build, update` и прочие. Необходимо использовать команды только с префиксом `new-`. Команды с этим префиксом совсем другие и они работают хорошо.
Вы должны увидеть, что файл `Dummy` скомпилировался.
После этого можно запустить `ghci`, и вызвать оттуда функцию `inc`, чтобы убедиться в её работе!
Итого:
```shell=
$ cabal new-update
$ cabal new-build
$ cabal new-repl
...
Prelude> inc 4
5
```
> Если хочется поиграться только с ghci, то шаг с `new-build` можно опускать. Он необходим, только если Вы будете создавать исполняемые файлы.
### Сравнение
В мире Haskell, к сожалению, очень давно не утихают споры, какой инструмент сборки проектов лучше. Вместо субъективных суждений, данный документ просто опишет некоторые особенности.
Термин `cabal` неоднозначный. В мире Haskell под ним может подразумеваться разное в зависимости от контекста:
1. `cabal` как `cabal-install` -- инструмент сборки проектов.
2. `cabal` как формат конфигурации проектов на Haskell с расширением `.cabal`. Вся конфигурация проекта (все модули проекта, зависимости проекта, метаданные) находятся в файле `project-name.cabal` и описана в специальном синтаксисе. Инструменты сборки (оба `cabal` и `stack`) используют информацию из этого файла для сборки проекта.
3. `cabal` как библиотека [`Cabal`](http://hackage.haskell.org/package/Cabal), которая парсит файлы в формате `.cabal`.
В этом документе и на лекциях под `cabal` неявно подразумевается `cabal-install`, а в остальных случаях идёт явное уточнение.
Все библиотеки, которые нужны вашему проекту, надо перечислять в поле `build-depends`. По умолчанию Ваш проект использует библиотеку `base` -- стандартную библиотеку. Но также есть, например, библиотека [`containers`](http://hackage.haskell.org/package/containers), содержащая типы данных `Map`, `Set` (реализованные на сбалансированных двоичных деревьев).
Если Вы используете `cabal`, то необходимо указывать нижнюю и верхнюю границу версий библиотек, с которыми Вам известно, что проект точно собирается.
Если Вы используете `stack`, то границы в `.cabal` файле указывать нет необходимости. `stack` использует информацию из `stack.yaml`. В этом файле указан `resolver`, который используется для сборки вашего проекта (например, `lts-10.5`). Resolver -- по сути снимок версий пакетов с Hackage. В вашем проекте будут использоваться только те версии, которые указаны в резолвере. Их можно найти здесь, например:
* https://www.stackage.org/lts-10.5
> В первом домашнем задании не потребуются библиотеки кроме `base`, поэтому пока можно сильно не задумываться о зависимостях.