Установка: opam, dune и REPL utop

Готовим рабочее окружение: менеджер пакетов opam, сборщик dune и интерактивную консоль utop.

opam — менеджер пакетов и версий компилятора OCaml; dune — система сборки; utop — улучшенный REPL для интерактивных экспериментов.

В мире OCaml экосистема собрана вокруг трёх инструментов, которые стоит понимать с самого начала. Это избавит от путаницы, типичной для новичков, которые пытаются запускать .ml-файлы напрямую и не понимают, где «main».

opam — фундамент

opam (OCaml Package Manager) делает то же, что cargo для Rust или pip+pyenv для Python вместе взятые: ставит библиотеки и управляет версиями самого компилятора.

# установка opam (Linux/macOS)
bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)"

# инициализация и установка компилятора
opam init
opam switch create 5.1.0
eval $(opam env)

# полезные инструменты
opam install dune utop ocaml-lsp-server

Понятие switch — это изолированное окружение с конкретной версией компилятора и набором пакетов, аналог виртуального окружения. Можно держать несколько switch'ей для разных проектов.

utop — площадка для экспериментов

utop — это REPL: вы вводите выражение, нажимаете ;; и Enter, получаете значение вместе с его выведенным типом. Это лучший способ изучать язык: каждое выражение немедленно показывает свой тип.

utop # 1 + 2 ;;
- : int = 3

utop # let greet name = "Привет, " ^ name ;;
val greet : string -> string = <fun>

utop # greet "Аня" ;;
- : string = "Привет, Аня"

Обратите внимание: вы нигде не написали типы, но utop сообщил, что greet имеет тип string -> string. Двойная точка с запятой ;; нужна только в REPL; в обычных файлах она почти не используется.

dune — сборка проектов

Для настоящих программ используют dune. Минимальный проект — это файл dune-project, файл dune с описанием цели и сам исходник:

(* файл: bin/main.ml *)
let () = Printf.printf "Сумма: %d\n" (2 + 3)
# dune-project
(lang dune 3.0)

# bin/dune
(executable (name main))
dune build      # собрать
dune exec bin/main.exe   # запустить

Вывод:

Сумма: 5

Как работает под капотом

Когда вы пишете dune build, dune строит граф зависимостей между модулями, вызывает компилятор в правильном порядке и кеширует результаты — повторная сборка пересобирает только изменённое. Файлы dune написаны на S-выражениях: это декларативное описание целей, а не императивный скрипт, поэтому dune может распараллеливать работу и точно отслеживать зависимости.

Частые ошибки

  • Забыли eval $(opam env) — тогда оболочка не видит dune/utop. Добавьте эту строку в профиль оболочки.
  • Ставят ;; в файлах. В .ml-файлах разделители не нужны; ;; — артефакт REPL.
  • Путают точку входа. В OCaml нет обязательной функции main; точка входа — верхнеуровневое выражение let () = ....

Итоги

  • opam управляет версиями компилятора (switch) и пакетами.
  • utop — REPL, который показывает значение и выведенный тип каждого выражения.
  • dune собирает проекты декларативно через файлы dune-project и dune.
Проверьте себя
1. Что делает opam в экосистеме OCaml?
AСобирает проекты в нативный код
BУправляет версиями компилятора и пакетами
CЯвляется REPL
DПроверяет типы
2. Зачем нужны два символа `;;` в utop?
AЭто комментарий
BЗавершают определение типа
CСигнал REPL, что выражение закончено и его нужно вычислить
DОбязательный разделитель в любом .ml-файле
3. Что является точкой входа в OCaml-программе?
AОбязательная функция main
BВерхнеуровневое выражение вида let () = ...
CФайл с именем entry.ml
DАннотация @entry