Проектирование больших проектов в OCaml
Что такое лучшие практики для написания больших программных проектов в OCaml?
Как вы структурируете свои проекты?
Какие функции OCaml следует и не следует использовать для упрощения управления кодом? Исключения? Первоклассные модули? GADTs? Типы объектов?
Сборка системы? Тестирование? Стек библиотеки?
Я нашел отличные рекомендации
Ответы
Ответ 1
Я собираюсь ответить за проект среднего размера в условиях, которые мне знакомы, то есть между 100 КБ и 1М строк исходного кода и до 10 разработчиков. Это то, что мы используем сейчас, для проекта, начатого два месяца назад в августе 2013 года.
Создание системы и организация кода:
- одна исходная оболочка script определяет PATH и другие переменные для нашего проекта
- один файл .ocamlinit в корне нашего проекта загружает кучу библиотек при запуске сеанса toplevel
- omake, который является быстрым (с опцией -j для параллельных построений); но мы избегаем создания сумасшедших пользовательских плагинов omake.
- один корень Файл Makefile содержит все основные цели (настройка, сборка, тестирование, очистка и т.д.)
- один уровень подкаталогов, а не два
- большинство подкаталогов встроены в библиотеку OCaml
- некоторые подкаталоги содержат другие вещи (настройка, сценарии и т.д.).
- OCAMLPATH содержит корень проекта; каждый подкаталог библиотеки создает META файл, делая все части проектов OCaml доступными из верхнего уровня, используя # require.
- для всего проекта построен только один исполняемый файл OCaml (экономит много времени на соединение, все еще не уверен, почему)
- библиотеки устанавливаются с помощью установки script с помощью opam
- локальные пакеты opam создаются для программного обеспечения, которое не находится в официальном репозитории opam
- мы используем переключатель opam, который является псевдонимом, названным в честь нашего проекта, избегая конфликтов с другими проектами на одной машине.
Редактирование исходного кода:
- emacs с пакетами opam ocp-indent и ocp-index
Управление и управление источниками:
- мы используем git и github
- весь новый код просматривается через запросы github pull
- tarballs для non-opam не-github-библиотек хранятся в отдельном репозитории git (который может быть удален, если история становится слишком большой)
- Существующие на github библиотеки с кровоточием разворачиваются в нашу учетную запись github и устанавливаются через наш собственный локальный пакет opam.
Использование OCaml:
- OCaml не будет компенсировать плохие методы программирования; преподавание хорошего вкуса выходит за рамки этого ответа. http://ocaml.org/learn/tutorials/guidelines.html является хорошей отправной точкой.
- OCaml 4.01.0 делает намного проще, чем раньше, для повторного использования меток полей полей и конструкторов вариантов (т.е.
type t1 = {x:int} type t2 = {x:int;y:int} let t1_of_t2 ({x}:t2) : t1 = {x}
теперь работает)
- мы стараемся не использовать расширения синтаксиса camlp4 в нашем собственном коде
- мы не используем классы и объекты, если это не предусмотрено какой-либо внешней библиотекой
- в теории, поскольку OCaml 4.01.0, мы должны предпочесть классические варианты над полиморфными вариантами.
- мы используем исключения, чтобы указывать на ошибки, и пусть они проходят счастливо до тех пор, пока наш основной цикл сервера не поймает их и не интерпретирует их как "внутреннюю ошибку" (по умолчанию), "плохой запрос" или что-то еще
- исключения, такие как Exit или Not_found, могут использоваться локально, когда это имеет смысл, но в интерфейсах модулей мы предпочитаем использовать параметры.
Библиотеки, протоколы, рамки:
- мы используем батареи для всех товарных функций, отсутствующих в стандартной библиотеке OCaml; для остальных у нас есть библиотека "util".
- мы используем Lwt для асинхронного программирования без расширений синтаксиса, а оператор привязки ( → =) является единственным оператором, который мы используем (если вам нужно знать, мы неохотно используем предварительную обработку camlp4 для лучшего отслеживания исключений при связывании точки).
- мы используем HTTP и JSON для связи с сторонним программным обеспечением, и мы ожидаем, что каждый современный сервис предоставит такие API.
- для обслуживания HTTP, мы запускаем собственный SCGI-сервер (ocaml-scgi) за nginx
- как клиент HTTP, мы используем Cohttp
- для сериализации JSON мы используем atdgen
"Облачные" услуги:
- мы используем довольно много, поскольку они обычно дешевы, легко взаимодействуют с ними и решают проблемы масштабируемости и обслуживания для нас.
Тестирование:
- У нас есть одна цель make/omake для быстрых тестов и одна для медленных тестов.
- быстрые тесты - это единичные тесты; каждый модуль может обеспечивать "тестовую" функцию; файл test.ml запускает список тестов
- медленными тестами являются те, которые включают запуск нескольких сервисов; они созданы специально для нашего проекта, но они охватывают как можно больше, как производственное обслуживание. Все работает локально либо в Linux, либо в MacOS, за исключением облачных сервисов, для которых мы находим способы не вмешиваться в производство.
Настройка этого всего довольно много, особенно для тех, кто не знаком с OCaml. Нет никакой инфраструктуры, которая все еще заботится обо всем этом, но по крайней мере вы получаете выбор инструментов.
Ответ 2
OASIS
Чтобы добавить к Павлу ответ:
Отказ от ответственности: я являюсь автором OASIS.
OASIS также имеет oasis2opam, который может помочь быстро создать пакет OPAM и oasis2debian для создания пакетов Debian. Это чрезвычайно полезно, если вы хотите создать цель "выпуска", которая автоматизирует большинство задач для загрузки пакета.
OASIS также поставляется с script под названием oasis-dist.ml, который автоматически создает tarball для загрузки.
Посмотрите все это в https://github.com/ocaml.org.
Тестирование
Я использую OUnit, чтобы выполнить все мои тесты. Это просто и довольно эффективно, если вы привыкли к тестированию xUnit.
Управление/управление источниками
Отказ от ответственности: Я являюсь владельцем/сопровождающим forge.ocamlcore.org(aka forge.o.o)
Если вы хотите использовать git, я рекомендую использовать github. Это действительно эффективно для обзора.
Если вы используете darcs или subversion, вы можете создать учетную запись на forge.o.o.
В обоих случаях наличие общего списка рассылки, в котором вы отправляете все уведомления о фиксации, обязательно, чтобы каждый мог их видеть и просматривать. Вы можете использовать либо группы Google, либо список рассылки на forge.o.o.
Я рекомендую иметь хорошую веб-страницу (github или forge.o.o) с сборкой документации OCamldoc каждый раз, когда вы совершаете. Если у вас огромная база кода, это поможет вам с самого начала использовать документацию, созданную OCamldoc (и быстро ее исправить).
Я рекомендую создавать tarballs, когда вы достигаете стабильной стадии. Не просто полагайтесь на проверку последней версии git/svn. Этот совет спас меня от работы в прошлом. Как сказал Мартин, сохраните все ваши tarballs в центральном месте (репозиторий git - хорошая идея для этого).
Ответ 3
Этот, вероятно, не полностью отвечает на ваш вопрос, но вот мой опыт в отношении среды сборки:
Я действительно ценю OASIS. Он имеет хороший набор функций, помогающий не только создавать проект, но и писать документацию и поддерживать тестовую среду.
Система сборки
- OASIS создает файл
setup.ml
из спецификации (_oasis
file), который работает в основном как здание script. Он принимает флаги -configure
, -build
, -test
, -distclean
. Я довольно привык к ним при работе с разными GNU и другими проектами, которые обычно используют Makefiles, и я считаю удобным, что здесь можно использовать все из них автоматически.
- Makefiles. Вместо генерации
setup.ml
можно также создать Makefile со всеми описанными выше опциями.
Структура
Обычно мой проект, созданный OASIS, имеет как минимум три каталога: src
, _build
, scripts
и tests
.
- В предыдущем каталоге все исходные файлы хранятся в одном каталоге: файлы источника (.ml) и интерфейса (.mli) хранятся вместе. Может быть, если проект слишком велик, стоит добавить дополнительные подкаталоги.
- Каталог
_build
находится под управлением системы сборки OASIS. Здесь хранятся как исходные, так и объектные файлы, и мне нравится, что файлы сборки не мешают исходным файлам, поэтому я могу легко удалить их, если что-то пойдет не так.
- Я храню несколько сценариев оболочки в каталоге
scripts
. Некоторые из них предназначены для выполнения тестов и создания файлов интерфейса.
- Все файлы ввода и вывода для тестов, которые я храню в отдельном каталоге.
Интерфейсы/документация
Использование файлов интерфейса (.mli) имеет как преимущества, так и недостатки для меня. Это действительно помогает находить ошибки типа, но если у вас есть они, вы должны их редактировать и при внесении изменений или улучшений в свой код. Иногда забывание об этом вызывает неприятные ошибки.
Но главная причина, по которой мне нравятся интерфейсные файлы, - это документация. Я использую ocamldoc для создания (OASIS поддерживает эту функцию с флагом -doc
) html страниц с документацией автоматически. На мой взгляд, достаточно написать комментарии, описывающие каждую функцию в интерфейсе, и не вставлять комментарии в середине кода. В OCaml функции обычно короткие и сжатые, и если есть необходимость вставлять туда дополнительные комментарии, возможно, лучше разделить функцию.
Также обратите внимание на флаг -i
для ocamlc
. Компилятор может автоматически генерировать файл интерфейса для модуля.
Испытания
Я не нашел разумного решения для поддержки тестов (я хотел бы иметь некоторое приложение ocamltest
), поэтому я использую свои собственные скрипты для выполнения и проверки вариантов использования. К счастью, OASIS поддерживает выполнение пользовательских команд, когда setup.ml
выполняется с флагом -test
.
Я долгое время не использую OASIS, и если кто-нибудь знает какие-либо другие интересные функции, я также хотел бы узнать о них.
Кроме того, вы не знаете OPAM, это определенно стоит посмотреть. Без него установка и управление новыми пакетами - это кошмар.