Глава 12 Расширения языка
8 Модульные псевдонимы на уровне типов
(Введено в OCaml 4.02)
|
Вышеприведенное описание, внутри спецификации, соответствует только определению модуля, равного путь_к_модулю. И наоборот, псевдоним модуля на уровне типов может соответствовать самому себе или любому типу-супертипу типа модуля, на который он ссылается.
Существуют определенные ограничения для пути_к_модулю:
- он должен иметь вид M0.M1...Mn (т.е. без применений функций);
- внутри тела функции M0 не должен быть одним из параметров функции;
- внутри рекурсивного определения модуля M0 не должен быть одним из рекурсивно определенных модулей.
Такие спецификации также выводятся. А именно, когда P — это путь, удовлетворяющий вышеуказанным ограничениям,
module N = P
имеет тип
module N = P
Псевдонимы модулей на уровне типов используются при проверке равенства путей к модулям. То есть, в контексте, где имя модуля N известно как псевдоним для P, эти два пути к модулю не только считаются равными, но и F(N) и F(P) также распознаются как равные. В режиме компиляции по умолчанию это единственное отличие от предыдущего подхода, где псевдонимы модулей имели просто тот же тип модуля, что и модули, на которые они ссылались.
При включении флага компилятора -no-alias-deps, псевдонимы модулей на уровне типов также используются для предотвращения введения зависимостей между единицами компиляции. А именно, псевдоним модуля, ссылающийся на модуль в другой единице компиляции, не вводит временную зависимость от этой единицы компиляции, до тех пор пока он не разыменован; он всё ещё вводит временную зависимость, если интерфейс нужно прочитать, то есть, если модуль является подмодулем единицы компиляции, или если какие-то компоненты типов упоминаются. Кроме того, доступ к псевдониму модуля вводит временную зависимость от единицы компиляции, содержащей модуль, на который ссылается псевдоним, а не от единицы компиляции, содержащей сам псевдоним. Обратите внимание, что эти различия в поведении времени компоновки могут быть несовместимы с предыдущим поведением, поскольку некоторые единицы компиляции могут не извлекаться из библиотек, а их побочные эффекты игнорируются.
Эти ослабленные зависимости позволяют использовать псевдонимы модулей вместо механизма -pack. Предположим, что у вас есть библиотека Mylib, состоящая из модулей A и B. Используя -pack, необходимо было бы выполнить команду
ocamlc -pack a.cmo b.cmo -o mylib.cmo
в результате чего была бы получена единица компиляции Mylib, содержащая физически A и B в качестве подмодулей и без зависимостей от их соответствующих единиц компиляции. Вот конкретный пример альтернативного подхода:
- Переименуйте файлы, содержащие A и B, в Mylib__A и Mylib__B.
- Создайте интерфейс паковки Mylib.ml, содержащий следующие строки.
module A = Mylib__A module B = Mylib__B
- Скомпилируйте Mylib.ml с помощью -no-alias-deps, а другие файлы — с помощью -no-alias-deps и -open Mylib (последнее эквивалентно добавлению строки open! Mylib в начало каждого файла).
ocamlc -c -no-alias-deps Mylib.ml ocamlc -c -no-alias-deps -open Mylib Mylib__*.mli Mylib__*.ml
- Наконец, создайте библиотеку, содержащую все единицы компиляции, и экспортируйте все скомпилированные интерфейсы.
ocamlc -a Mylib*.cmo -o Mylib.cma
Этот подход позволяет напрямую обращаться к A и B внутри библиотеки и как Mylib.A и Mylib.B извне. У него также есть преимущество, что Mylib больше не монолитна: если вы используете Mylib.A, будет подключаться только Mylib__A, а не Mylib__B.
Обратите внимание на использование двойных подчеркиваний в Mylib__A и Mylib__B. Это было сделано намеренно; компилятор использует следующий эвристический подход при печати путей: задан путь Lib__fooBar, если Lib.FooBar существует и является псевдонимом для Lib__fooBar, то компилятор всегда будет отображать Lib.FooBar вместо Lib__fooBar. Таким образом, длинные имена с префиксом Mylib__ остаются скрытыми, а все, что видит пользователь, это более удобные имена с точками. Так компилируется стандартная библиотека OCaml.
© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/modulealias.html