Методические указания

Групповая работа

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

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

Для эффективной работы (в том числе в одиночку) предлагается:

  • обсуждать структуру проекта и каждой части до реализации (работать вместе проще, когда вы работаете в одном направлении);
  • разбивать реализацию на логические модули (например, индивидуальные части заданий должны находиться в разных модулях и минимально зависеть друг от друга);
  • пользоваться системой контроля версий (например, Git);
  • придерживаться правил оформления исходного кода (хорошо написанный код легко читать, проверять и модифицировать);
  • проводить просмотр кода (code review) других участников проекта (это позволяет улучшить общее представление о проекте, уменьшает шанс логических и других ошибок в общем проекте, позволяет поддерживать исходный код оформленным).

Правила оформления исходного кода

Программирование — это в равной мере искусство и ремесло. И, как известно любому художнику, ограничения расширяют, а не подавляют творческие способности.

Правила оформления исходного кода:

  • Используйте camelCase для именования функций и переменных.
  • Используйте описательные названия функций, пусть они будут настолько длинные, насколько необходимо, но не длиннее этого. Хорошо: solveRemaining. Плохо: slv. Ужасно: solveAllTheCasesWhichWeHaven'tYetProcessed.
  • НЕ используйте транслит в названиях. Это является индикатором непрофессионализма и скорее всего приведёт к высмеиванию кода вашими коллегами в любом нормальном месте работы.
  • НЕ используйте символы табуляции: Haskell чувствителен к отступам и табуляция может сильно испортить вам жизнь. Заметьте, что это не значит, что вам придётся нажимать пробел миллион раз: ваш любимый текстовый редактор умеет заменять табуляцию на пробелы автоматически.
  • Старайтесь не писать строчки длиннее 80 символов. Код, который не приходится пролистывать по горизонтали читать обычно удобнее.
  • Описывайте тип для каждой функции на верхнем уровне. Сигнатура типа значительно улучшает документацию и способствует правильному мышлению. Также явное указание типов способствует лучшим сообщениям об ошибках при компиляции. Локально определенные функции и константы (определенные при помощи let и where) не нуждаются в сигнатурах, но их выписывание не навредит (это также улучшает сообщения об ошибках).
  • Формулируйте развёрнутый комментарий для каждой функции на верхнем уровне.
  • Используйте флаг -Wall при компиляции или напишите {-# OPTIONS_GHC -Wall #-} в самом начале файла с кодом. Этот флаг включает все предупредительные сообщения компилятора.
  • По возможности разбивайте ваш код на простые функции (каждая с одним ясным предназначением) и составляйте из них программу.
  • Пишите всюду определённые функции: они не должны завершаться аварийно ни на одном возможном входе.

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

Для разработки проекта рекомендуется использовать Stack.

Stack может установить нужную версию компилятора и необходимых библиотек. Для установки компилятора необходимо запустить команду stack setup.

Для установки зависимостей Stack использует Stackage — стабильный репозиторий пакетов. Для выполнения практического задания рекомендуется использовать последний доступный LTS Haskell.

Для сборки проекта используйте команду stack build.

Для запуска интерпретатора GHCi с автоматической загрузкой модулей проекта используйте команду stack ghci.

Для запуска исполняемой программы (например, графического интерфейса) используйте команду stack run.

Рекомендации по оформлению задания

Каждое практическое задание можно реализовать множеством способов с различными наборами возможностей. Для того, чтобы другим людям (в том числе соучастникам и преподавателям) было проще разобраться в законченном проекте, рекомендуется:

  • добавить файл README, включающий в себя:
  • описание проекта;
  • инструкцию по установке и запуску;
  • описание реализованных возможностей;
  • оформить код в виде проекта Stack:
  • для сборки проекта должно быть достаточно выполнить команду stack build;
  • запуск интерпретатора GHCi с автоматической загрузкой модулей проекта осуществляется командой stack ghci;
  • запуск исполняемого файла проекта — командой stack run.
  • тестирование проекта осуществляется командой stack test.

Рекомендации по выбору библиотек

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

Синтаксический разбор

Для синтаксического разбора рекомендуется использовать комбинаторные библиотеки — например, Parsec или attoparsec. Parsec предоставляет более выразительные средства и лучше подходит для разбора исходного кода и конфигурационных файлов. attoparsec предлагает более простой интерфейс и меньше возможностей, но на несколько порядков лучше по производительности и подходит для разбора сетевых протоколов, логов, бинарных данных.

Интерфейс

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

Библиотека gloss предоставляет простой и удобный интерфейс для работы с векторной 2D графикой. Для игр рекомендуется использование модулей Graphics.Gloss.Interface.Pure.Game или Graphics.Gloss.Interface.IO.Game. Для моделирования можно использовать модули Graphics.Gloss.Interface.Pure.Simulate или Graphics.Gloss.Interface.IO.Simulate.

Конфигурационные файлы

Для написания конфигурационных файлов для приложения существует множество языков разметки и библиотек на Haskell для их перевода во внутреннее представление. Наиболее распространёнными являются следующие:

Клиент-серверная архитектура

Для большинства практических заданий в качестве протокола общения между клиентом и сервером можно использовать HTTP. При реализации HTTP сервера рекомендуется использовать архитектуру REST.

Существует множество web-фреймворков для реализации серверной части. Для выполнения практических заданий рекомендуется использовать использовать один из следующих:

  • servant — относительно простой в использовании и в то же время мощный фреймворк для работы с REST API; в отличие от большинства других фреймворков покрывает не только серверную, но и клиентскую части, а так же автоматическую документацию, инструменты для тестирования, генерация клиентского кода для других языков программирования;
  • spock — неплохой фреймворк с неплохой документацией; некоторые возможности требуют хорошенько разобраться, но для выполнения практического задания они необязательны; использовать в паре с wreq для клиентской части;
  • scotty — наверное, самый простой фреймворк; использовать в паре с wreq для клиентской части;

Для более тесной связи клиента и сервера рекомендуется использовать веб-сокеты. Соответствующая библиотека — websockets.

Для передачи данных по сети рекомендуется использовать сериализацию/десериализацию данных. В случае HTTP предлагается использовать формат JSON (используя библиотеку aeson). В случае веб-сокетов — бинарное представление (используя библиотеку binary).

База данных

Для работы с базой данных рекомендуется использовать библиотеку persistent. Эта библиотека предоставляет интерфейс, не зависящий от конкретной используемой СУБД и поддерживает как минимум PostgreSQL, SQLite, MySQL и MongoDB. Для сложных запросов (например, по нескольким таблицам) предлагается использовать библиотеку esqueleto, которая работает поверх библиотеки persistent.

Генерация кода

Для генерации объектного кода проще всего использовать существующий низкоуровневый язык программирования, из которого уже можно легко получить объектный код. К таким языкам относятся C, C–– и язык LLVM. Последний часто используется в компиляторах, поскольку специально создан для этой цели.

Генерация кода для LLVM на Haskell реализуется при помощи библиотеки llvm-general.