# 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`, поэтому пока можно сильно не задумываться о зависимостях.