Учебник OCaml для начинающих
OCaml — функциональный язык семейства ML, на котором пишут торговые системы Jane Street, компиляторы и системы верификации. Этот курс ведёт от установки и базовых выражений к сильному выводу типов, сопоставлению с образцом, алгебраическим типам и мощнейшей в индустрии системе модулей и функторов. По пути мы проводим параллели с Haskell, F# и Scala и показываем, чем OCaml прагматичнее: энергичные вычисления, разрешённая мутация и компиляция в быстрый нативный код.
Курс «OCaml: практичный функциональный язык» состоит из 8 разделов и 25 уроков: Знакомство с OCaml, Типы и вывод типов, Функции, Данные: кортежи, записи, варианты, Сопоставление с образцом, Списки, полиморфизм, ошибки, Модули и функторы и Прагматизм: изменяемость, ООП, производительность. Уроки идут по порядку — от основ к более сложным темам, в каждом есть объяснение с примерами, а в конце — вопросы для самопроверки. К урокам привязаны задачи с автоматической проверкой: прочитали тему — сразу закрепили её кодом.
Программа курса
1 Знакомство с OCaml
- Что такое OCaml и зачем он нужен
OCaml — прагматичный функциональный язык семейства ML: статическая типизация с выводом типов, скорость нативного кода, мультипарадигменность.
- Установка: opam, dune и REPL utop
Ставим OCaml через opam, знакомимся со сборщиком dune и интерактивным REPL utop для экспериментов с кодом.
- Всё — выражение: let и значения
В OCaml почти всё является выражением, возвращающим значение. Разбираем let-связывания, локальные let ... in и отличие от переменных.
- Что такое OCaml и зачем он нужен
2 Типы и вывод типов
- Сильный вывод типов: Хиндли — Милнер
Алгоритм Хиндли — Милнера выводит типы без аннотаций. Как компилятор OCaml угадывает самый общий тип каждого выражения и почему это безопасно.
- Базовые типы: int, float и раздельная арифметика
int и float в OCaml — разные типы с разными операторами: + и +., * и *.. Зачем это сделано и как избежать ошибок арифметики.
- Строки, символы, логика и unit
Работа со строками (^, индексация), символами char, логическими операторами && и ||, и роль типа unit.
- Сильный вывод типов: Хиндли — Милнер
3 Функции
- Функции, каррирование и частичное применение
Функции в OCaml каррированы: каждая принимает один аргумент и возвращает функцию. Это даёт элегантное частичное применение.
- Конвейер |> и композиция функций
Оператор |> передаёт значение в функцию слева направо, делая код читаемым как цепочку преобразований. Композиция и оператор @@.
- Рекурсия: let rec и хвостовые вызовы
Рекурсивные функции требуют ключевого слова rec. Обычная и хвостовая рекурсия, аккумуляторы и почему хвостовая рекурсия не переполняет стек.
- Функции, каррирование и частичное применение
4 Данные: кортежи, записи, варианты
- Кортежи и записи
Кортежи группируют значения по позиции, записи (records) — по именованным полям. Создание, доступ к полям и неизменяемость.
- Алгебраические типы: моделируем домен
Вариантные типы (sum types) позволяют точно описать множество состояний домена. Конструкторы с данными и сила исчерпывающего моделирования.
- option вместо null
Тип option (Some/None) делает отсутствие значения явным в системе типов, устраняя целый класс ошибок NullPointerException.
- Кортежи и записи
5 Сопоставление с образцом
- match: основы сопоставления с образцом
Сопоставление с образцом match — центральная конструкция OCaml. Синтаксис, разбор вариантов, кортежей и связывание переменных.
- Исчерпывающность и сторожевые условия when
Компилятор проверяет, что match покрывает все случаи. Предупреждения об исчерпывающности, охранные условия when и образцы с |.
- match: основы сопоставления с образцом
6 Списки, полиморфизм, ошибки
- Списки и модуль List
Списки — фундаментальная структура OCaml. Устройство (head/tail), конструктор ::, рекурсивная обработка и ключевые функции List.
- map, filter и fold
Три кита обработки коллекций: map преобразует, filter отбирает, fold сворачивает в одно значение. fold_left и fold_right.
- Параметрический полиморфизм
Полиморфные функции работают с любым типом через переменные типа 'a. Параметрический полиморфизм и принцип параметричности.
- Обработка ошибок: result против исключений
OCaml предлагает два пути обработки ошибок: тип result для ожидаемых сбоев и исключения для исключительных ситуаций.
- Списки и модуль List
7 Модули и функторы
- Модули и сигнатуры
Модули группируют типы и функции, сигнатуры задают интерфейс и скрывают детали реализации. module, struct/sig и инкапсуляция.
- Функторы: модули, параметризованные модулями
Функтор — это функция от модуля к модулю. Как параметризовать модуль другим модулем (множества, сортировка) и зачем это нужно.
- Организация проекта и экосистема dune
Как устроен реальный OCaml-проект: файлы .ml/.mli, библиотеки и исполняемые файлы в dune, подключение пакетов через opam, тестирование.
- Модули и сигнатуры
8 Прагматизм: изменяемость, ООП, производительность
- Изменяемость: ref, mutable и массивы
OCaml прагматичен: когда нужна мутация, есть ref-ячейки, mutable-поля записей и массивы с быстрым доступом по индексу.
- Императивные конструкции: for, while, побочные эффекты
Циклы for и while, последовательность действий через ; и печать. Когда императивный стиль в OCaml уместнее рекурсии.
- ООП в OCaml: классы и объекты
В OCaml есть полноценная объектная система: классы, объекты, наследование и структурная типизация. Почему она используется редко.
- Производительность, компиляция и место OCaml
Нативная компиляция, сборщик мусора и итоговое сравнение OCaml с Haskell, F# и Scala. Где OCaml — лучший выбор.
- Изменяемость: ref, mutable и массивы