Spec-Zone.ru › OCaml
☰Инструменты OCaml
  • Пакетная компиляция (ocamlc)
  • Топовый уровень или REPL (ocaml)
  • Система выполнения (ocamlrun)
  • Компиляция в машинный код (ocamlopt)
  • Генераторы лексического и синтаксического анализа (ocamllex, ocamlyacc)
  • Генератор зависимостей (ocamldep)
  • Генератор документации (ocamldoc)
  • Отладчик (ocamldebug)
  • Профилирование (ocamlprof)
  • Интерфейс C с OCaml
  • Оптимизация с помощью Flambda
  • Мультирование с afl-fuzz
  • Отслеживание выполнения с помощью событий runtime
  • Преобразование программы «Tail Modulo Constructor»
  • Обнаружение гонок данных в runtime с ThreadSanitizer

Глава 13. Пакетная компиляция (ocamlc)

В этой главе описывается пакетный компилятор OCaml ocamlc, который компилирует исходные файлы OCaml в файлы-объекты байткода и связывает эти файлы-объекты, чтобы создать автономные исполняемые файлы байткода. Эти исполняемые файлы затем запускаются интерпретатором байткода ocamlrun.

1 Обзор компилятора

Команда ocamlc имеет интерфейс командной строки, похожий на большинство компиляторов C. Она принимает несколько типов аргументов и обрабатывает их последовательно, после обработки всех опций:

  • Аргументы, заканчивающиеся на .mli, считаются исходными файлами интерфейсов единиц компиляции. Интерфейсы определяют имена, экспортируемые единицами компиляции: они объявляют имена значений с их типами, определяют публичные типы данных, объявляют абстрактные типы данных и так далее. Из файла x.mli компилятор ocamlc создает скомпилированный интерфейс в файле x.cmi.
  • Аргументы, заканчивающиеся на .ml, считаются исходными файлами реализации единиц компиляции. Реализации предоставляют определения для экспортируемых имён, а также содержат выражения, которые должны оцениваться для их побочных эффектов. Из файла x.ml компилятор ocamlc генерирует скомпилированный байт-код в файле x.cmo.

    Если файл интерфейса x.mli существует, реализация x.ml проверяется по отношению к соответствующему скомпилированному интерфейсу x.cmi, который предполагается существующим. Если интерфейс x.mli не указан, компиляция x.ml создаёт файл скомпилированного интерфейса x.cmi в дополнение к файлу скомпилированного объектного кода x.cmo. Файл x.cmi соответствует интерфейсу, экспортирующему всё, что определено в реализации x.ml.

  • Аргументы, заканчивающиеся на .cmo, считаются скомпилированными объектами байткода. Эти файлы связываются вместе с файлами-объектами, полученными путём компиляции аргументов .ml (если таковые имеются), и стандартной библиотекой OCaml, чтобы создать автономную исполняемую программу. Порядок, в котором аргументы .cmo и .ml представлены в командной строке, имеет значение: единицы компиляции инициализируются в том порядке, в котором они указаны. Поэтому, данному файлу x.cmo должны предшествовать все файлы .cmo, которые ссылаются на единицу x.
  • Аргументы, заканчивающиеся на .cma, считаются библиотеками байткода объектов. Библиотека байткода объектов упаковывает в один файл набор файлов-объектов (.cmo). Библиотеки создаются с помощью ocamlc -a (см. описание опции -a ниже).
  • Аргументы, заканчивающиеся на .c, передаются компилятору C, который генерирует файл-объект .o (под Windows .obj). Этот файл-объект связывается с программой, если установлен флаг -custom (см. описание -custom ниже).
  • Аргументы, заканчивающиеся на .o или .a (под Windows .obj или .lib), считаются файлами-объектами и библиотеками C. Они передаются в компоновщик C при компоновке в режиме -custom (см. описание -custom ниже).
  • Аргументы, заканчивающиеся на .so (под Windows .dll) считаются динамическими библиотеками C (DLL). Во время компоновки они ищутся для внешних функций C, на которые ссылается код OCaml, и их имена записываются в сгенерированный исполняемый файл байткода. Система выполнения ocamlrun затем загружает их динамически во время запуска программы.

Выходной файл фазы компоновки — файл, содержащий скомпилированный байткод, который может быть выполнен интерпретатором байткода OCaml: командой, названной ocamlrun.

        ocamlrun a.out arg1 arg2 … argn

выполняет скомпилированный код, содержащийся в a.out, передавая ему в качестве аргументов строковые значения arg1 до argn. (См. главу ‍15 для получения дополнительной информации.)

На большинстве систем файл, созданный на этапе компоновки, может быть запущен непосредственно, как в примере:

        ./a.out arg1 arg2 … argn

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

Компилятор может выводить информацию о своих внутренних этапах. Он может генерировать файлы .cmt для реализации единицы компиляции и .cmti для сигнатур, если ему передана опция -bin-annot (см. описание -bin-annot ниже). Каждый такой файл содержит типизированное абстрактное синтаксическое дерево (AST), которое генерируется во время проверки типов. Это дерево содержит всю доступную информацию о местоположении и конкретном типе каждого термина в исходном файле. AST является частичным, если проверка типов не удалась.

Эти файлы .cmt и .cmti обычно полезны для инструментов проверки кода.

2 Опции

Следующие опции командной строки распознаются компилятором ocamlc. Опции -pack, -a, -c, -output-obj и -output-complete-obj взаимоисключают друг друга.

-a
Создайте библиотеку (файл .cma) из объектных файлов (файлов .cmo), переданных в командной строке, вместо их компоновки в исполняемый файл. Имя библиотеки должно быть установлено с помощью опции -o.

Если в командной строке переданы опции -custom, -cclib или -ccopt, эти опции сохраняются в результирующей библиотеке .cma. Затем компоновка с этой библиотекой автоматически добавляет обратно опции -custom, -cclib и -ccopt, как если бы они были указаны в командной строке, если не задана опция -noautolink.

-absname
Вынуждает сообщения об ошибках показывать абсолютные пути к именам файлов.
-no-absname
Не пытаться показывать абсолютные имена файлов в сообщениях об ошибках.
-annot
Устарело начиная с OCaml 4.11. Пожалуйста, используйте -bin-annot вместо этого.
-args filename
Считывать дополнительные аргументы командной строки, завершаемые символом новой строки, из файла filename.
-args0 filename
Считывать дополнительные аргументы командной строки, завершаемые нулевым символом, из файла filename.
-bin-annot
Выводить подробную информацию о компиляции (типы, связи, хвостовые вызовы и т. д.) в двоичном формате. Информация для файла src.ml (соответственно src.mli) помещается в файл src.cmt (соответственно src.cmti). В случае ошибки типизации вывести всю информацию, полученную проверяющим типов, перед ошибкой. Файлы *.cmt и *.cmti, созданные с помощью -bin-annot, содержат больше информации и гораздо компактнее, чем файлы, созданные с помощью -annot.
-c
Только компиляция. Игнорировать этап компоновки компиляции. Исходные файлы преобразуются в скомпилированные файлы, но исполняемый файл не создаётся. Эта опция полезна для компиляции модулей по отдельности.
-cc ccomp
Использовать ccomp как компоновщик C при компоновке в режиме «пользовательского времени выполнения» (см. опцию -custom) и как компилятор C для компиляции файлов исходного кода .c. При компоновке объектных файлов, сгенерированных компилятором C++ (таким как g++ или clang++), рекомендуется использовать -cc c++.
-cclib -llibname
Передать опцию -llibname компоновщику C при компоновке в режиме «пользовательского времени выполнения» (см. опцию -custom). Это приводит к подключению указанной библиотеки C к программе.
-ccopt option
Передать данную опцию компилятору и компоновщику C. Например, при компоновке в режиме «пользовательского времени выполнения» -ccopt -Ldir заставляет компоновщик C искать библиотеки C в каталоге dir. (См. опцию -custom.)
-cmi-file filename
Используйте указанный интерфейсный файл для проверки типа ML-файла исходного кода для компиляции. Если эта опция не указана, компилятор ищет файл .mli с тем же базовым именем, что и реализация, которую он компилирует, и в том же каталоге. Если такой файл найден, компилятор ищет соответствующий файл .cmi в включённых каталогах и сообщает об ошибке, если не найдёт его.
-color mode
Включить или выключить цвета в сообщениях компилятора (особенно предупреждения и ошибки). Поддерживаются следующие режимы:
auto
использовать эвристику, чтобы включить цвета только в том случае, если вывод их поддерживает (терминал, совместимый с ANSI);
always
включить цвета безусловно;
never
отключить вывод цвета.

Переменная окружения OCAML_COLOR учитывается, если -color не указана. Её значения - auto/always/never, как указано выше.

Если -color не указана, OCAML_COLOR не установлена, а переменная окружения NO_COLOR установлена, вывод цвета отключен. В противном случае, значение по умолчанию — 'auto', и текущая эвристика проверяет, существует ли переменная окружения TERM и не пуста ли она или dumb, а также то, что 'isatty(stderr)' выполняется.

-error-style mode
Управлять способом вывода сообщений об ошибках и предупреждений. Поддерживаются следующие режимы:
short
выводить только ошибку и её расположение;
contextual
как short, но также отображать фрагмент исходного кода, соответствующий расположению ошибки.
Значение по умолчанию — contextual.

Переменная окружения OCAML_ERROR_STYLE учитывается, если -error-style не указана. Её значения - short/contextual, как указано выше.

-compat-32
Проверить, может ли сгенерированный исполняемый байт-код работать на 32-битных платформах, и сообщить об ошибке, если нет. Это полезно при компиляции байт-кода на 64-битном компьютере.
-config
Вывести номер версии ocamlc и подробное описание его конфигурации, затем выйти.
-config-var var
Вывести значение определённой переменной конфигурации из вывода -config, затем выйти. Если переменная не существует, код выхода — ненулевой. Эта опция доступна только начиная с OCaml 4.08, поэтому авторы скриптов должны иметь резервный вариант для более старых версий.
-custom
Ссылка в режиме «пользовательская среда выполнения». В стандартном режиме ссылки компоновщик генерирует байткод, предназначенный для выполнения со стандартной системой выполнения, ocamlrun. В режиме пользовательской среды выполнения компоновщик генерирует выходной файл, содержащий как систему выполнения, так и байткод для программы. Результирующий файл больше, но его можно выполнить напрямую, даже если команда ocamlrun не установлена. Кроме того, режим «пользовательская среда выполнения» позволяет статически связывать код OCaml с пользовательскими функциями C, как описано в главе ‍22.
Unix: Никогда не используйте команду strip с исполняемыми файлами, созданными с помощью ocamlc -custom, так как это удалит часть байткода в исполняемом файле.
Unix: Предупреждение о безопасности: никогда не устанавливайте биты «setuid» или «setgid» для исполняемых файлов, созданных с помощью ocamlc -custom, так как это сделает их уязвимыми для атак.
-depend ocamldep-args
Вычислять зависимости, как это делала бы команда ocamldep. Остальные аргументы интерпретируются так, как будто они были переданы команде ocamldep.
-dllib -llibname
Обеспечить динамическую загрузку разделяемой библиотеки C dlllibname.so ( dlllibname.dll под Windows) системой выполнения ocamlrun при запуске программы.
-dllpath dir
Добавляет директорию dir в путь поиска разделяемых библиотек C во время выполнения. Во время компоновки разделяемые библиотеки ищутся в стандартном пути (соответствующем параметру -I). Параметр -dllpath просто сохраняет dir в сгенерированном исполняемом файле, где ocamlrun может его найти и использовать, как описано в разделе ‍15.3.
-for-pack module-path
Сгенерировать файл объекта (.cmo), который позже может быть включен как подмодуль (с указанным путем доступа) в единицу компиляции, созданную с помощью -pack. Например, ocamlc -for-pack P -c A.ml сгенерирует a..cmo, которое позже можно использовать с ocamlc -pack -o P.cmo a.cmo. Примечание: Вы по-прежнему можете упаковать модуль, который был скомпилирован без -for-pack, но в этом случае исключения будут выведены с неправильными именами.
-g
Добавить отладочную информацию во время компиляции и компоновки. Этот параметр необходим для отладки программы с помощью ocamldebug (см. главу ‍20) и для вывода стэка вызовов при завершении программы с необработанным исключением (см. раздел ‍15.2).
-no-g
Не записывать отладочную информацию (по умолчанию).
-i
Заставить компилятор выводить все определенные имена (с их выведенными типами или определениями) при компиляции реализации (.ml файла). Никаких скомпилированных файлов (.cmo и .cmi файлы) не генерируются. Это может быть полезно для проверки типов, выведенных компилятором. Кроме того, поскольку вывод соответствует синтаксису интерфейсов, это может помочь при написании явного интерфейса (.mli файл): просто перенаправьте стандартный вывод компилятора в .mli файл и отредактируйте этот файл, чтобы удалить все объявления неэкспортированных имен.
-I directory
Добавить указанную директорию в список директорий, которые просматриваются при поиске скомпилированных файлов интерфейсов (.cmi), скомпилированных файлов объектного кода .cmo, библиотек (.cma) и библиотек C, указанных с помощью -cclib -lxxx. По умолчанию сначала просматривается текущая директория, затем — стандартная директория библиотек. Директории, добавленные с помощью -I, просматриваются после текущей директории в том порядке, в котором они были заданы в командной строке, но перед стандартной директорией библиотек. См. также параметр -nostdlib.

Если заданная директория начинается с +, она воспринимается относительно стандартной директории библиотек. Например, -I +unix добавляет поддиректорию unix стандартной библиотеки в путь поиска.

-H directory
Ведёт себя идентично -I, за исключением (а) программы не могут напрямую ссылаться на модули, добавленные в путь поиска таким способом, и (б) эти директории просматриваются после любых директорий -I. Это позволяет предоставить компилятору скомпилированные файлы интерфейсов и объектного кода для транзитивных зависимостей текущей программы (зависимости её зависимостей), не позволяя им стать прямыми зависимостями.
-impl filename
Компилировать файл filename как файл реализации, даже если его расширение не .ml.
-intf filename
Компилировать файл filename как файл интерфейса, даже если его расширение не .mli.
-intf-suffix string
Распознавать имена файлов, заканчивающиеся на string, как файлы интерфейсов (вместо стандартного .mli).
-labels
Метки не игнорируются в типах, метки могут использоваться в приложениях, и помеченные параметры могут быть заданы в любом порядке. Это значение по умолчанию.
-linkall
Принудительно связывать все модули, содержащиеся в библиотеках. Если этот флаг не задан, неиспользуемые модули не связываются. При построении библиотеки (параметр -a), установка параметра -linkall заставляет последующие связи программ, включающих эту библиотеку, связывать все модули, содержащиеся в библиотеке. При компиляции модуля (параметр -c), установка параметра -linkall гарантирует, что этот модуль всегда будет связан, если он помещён в библиотеку, а эта библиотека связана.
-make-runtime
Построение пользовательской системы выполнения (в файле, указанном параметром -o), включающей файлы и библиотеки C-объектов, заданные в командной строке. Эту пользовательскую систему выполнения можно использовать позже для выполнения исполняемых файлов байткода, созданных с помощью параметра ocamlc -use-runtime runtime-name. Дополнительную информацию см. в разделе ‍22.1.6.
-match-context-rows
Установите количество строк контекста, используемых для оптимизации во время компиляции сопоставления с образцом. Значение по умолчанию 32. Более низкие значения приводят к более быстрому компилятору, но менее оптимизированному коду. Этот расширенный параметр предназначен для использования в случае, если программа, интенсивно использующая сопоставление с образцом, приводит к значительному увеличению времени компиляции.
-no-alias-deps
Не записывать зависимости для псевдонимов модулей. Дополнительную информацию см. в разделе ‍12.8.
-no-app-funct
Деактивирует прикладное поведение функторов. С этим параметром каждое применение функтора генерирует новые типы в результате, и применение того же функтора дважды к одному и тому же аргументу даёт две несовместимые структуры.
-noassert
Не компилировать проверки утверждений. Обратите внимание, что специальная форма assert false всегда компилируется, потому что она имеет специальный тип. Этот флаг не имеет эффекта при компоновке уже скомпилированных файлов.
-noautolink
При компоновке библиотек .cma игнорировать параметры -custom, -cclib и -ccopt, которые могут содержаться в библиотеках (если эти параметры были заданы при построении библиотек). Это может быть полезно, если библиотека содержит некорректные спецификации C-библиотек или C-параметров; в этом случае во время компоновки задайте -noautolink и передайте правильные C-библиотеки и параметры в командной строке.
-nolabels
Игнорировать необязательные метки в типах. Метки нельзя использовать в приложениях, и порядок параметров становится строгим.
-nostdlib
Не включать директорию стандартной библиотеки в список директорий, которые проверяются при поиске скомпилированных файлов интерфейса (.cmi), скомпилированных файлов объектного кода (.cmo), библиотек (.cma) и C-библиотек, указанных с помощью -cclib -lxxx. См. также параметр -I.
-o output-file
Указать имя выходного файла. Для исполняемых файлов имя по умолчанию — a.out в Unix и camlprog.exe в Windows. Если используется параметр -a, указать имя создаваемой библиотеки. Если используется параметр -pack, указать имя создаваемого упакованного файла объекта. Если используются параметры -output-obj или -output-complete-obj, указать имя создаваемого файла объекта. Если используется параметр -c, указать имя файла объекта, создаваемого для следующего исходного файла, указанного в командной строке.
-opaque
При компиляции реализации родным компилятором по умолчанию создаётся файл .cmx с информацией для межмодульной оптимизации. Также ожидаются файлы .cmx для зависимостей текущего компилируемого исходного файла, и они используются для оптимизации. С OCaml 4.03 компилятор выводит предупреждение, если не может найти файл .cmx для одной из этих зависимостей.

Параметр -opaque, доступный с версии 4.04, отключает информацию о межмодульной оптимизации для текущей компилируемой единицы. При компиляции интерфейса .mli с помощью -opaque отмечается скомпилированный интерфейс .cmi так, что последующие компиляции модулей, которые зависят от него, не будут полагаться на соответствующий файл .cmx и не будут выдавать предупреждений об его отсутствии. При компиляции реализации .ml с помощью -opaque генерируется файл .cmx, который не содержит никакой информации о межмодульной оптимизации.

Использование этого параметра может ухудшить качество генерируемого кода, но сокращает время компиляции как при чистых, так и при инкрементальных сборках. Действительно, при использовании родного компилятора, когда изменяется реализация единицы компиляции, могут потребоваться перекомпиляции всех единиц, которые от неё зависят — потому что информация о межмодульной оптимизации может измениться. Если единица компиляции, чья реализация изменилась, была скомпилирована с помощью -opaque, такая перекомпиляция не требуется. Этот параметр может использоваться, например, для получения более быстрых циклов редактирования-компиляции-тестирования.

-open Module
Открывает указанный модуль перед обработкой файлов интерфейса или реализации. Если задано несколько параметров -open, они обрабатываются в порядке следования, так же, как если бы были добавлены операторы open! Module1;; ... open! ModuleN;; в начало каждого файла.
-output-obj
Заставляет компоновщик создавать файл C-объекта вместо исполняемого файла байткода. Это полезно для обертывания OCaml-кода в C-библиотеку, вызываемую любой C-программой. См. главу ‍22, раздел ‍22.7.5. Имя выходного файла объекта должно быть задано параметром -o. Этот параметр также может быть использован для создания файла C-исходного кода (.c расширение) или скомпилированной разделяемой/динамической библиотеки (.so расширение, .dll в Windows).
-output-complete-exe
Создать автономный исполняемый файл, скомпоновав файл C-объекта, содержащий программу байткода, систему времени выполнения OCaml и любой другой статический C-код, предоставленный команде ocamlc. Полученный эффект аналогичен -custom, за исключением того, что байткод встраивается в C-код, поэтому он больше недоступен для инструментов, таких как ocamldebug. С другой стороны, получившийся двоичный файл устойчив к действию strip.
-output-complete-obj
То же, что и параметр -output-obj, за исключением того, что создаваемый файл объекта включает в себя систему времени выполнения и библиотеки автоссылки.
-pack
Создать файл объекта байткода (.cmo файл) и соответствующий скомпилированный интерфейс (.cmi), объединяющий файлы объектов, заданные в командной строке, делая их подмодулями выходного файла .cmo. Имя выходного файла .cmo должно быть задано параметром -o. Например,
        ocamlc -pack -o p.cmo a.cmo b.cmo c.cmo
генерирует скомпилированные файлы p.cmo и p.cmi, описывающие единицу компиляции, содержащую три подмодуля A, B и C, соответствующие содержимому файлов объектов a.cmo, b.cmo и c.cmo. Это содержимое можно использовать как P.A, P.B и P.C в остальной части программы.
-pp command
Заставляет компилятор вызывать заданную команду как препроцессор для каждого исходного файла. Выход команды перенаправляется в промежуточный файл, который компилируется. При отсутствии ошибок компиляции промежуточный файл удаляется после этого.
-ppx command
После разбора, абстрактное синтаксическое дерево передаётся препроцессору command. Модуль Ast_mapper, описанный в главе ‍30: Ast_mapper , реализует внешний интерфейс препроцессора.
-principal
Проверять пути информации во время проверки типов, чтобы убедиться, что все типы получены принципиальным способом. При использовании помеченных аргументов и/или полиморфных методов этот флаг необходим для обеспечения того, что будущие версии компилятора смогут корректно вывести типы, даже если внутренние алгоритмы изменятся. Все программы, принятые в режиме -principal, также принимаются в режиме по умолчанию с эквивалентными типами, но различными двоичными подписями, что может замедлить проверку типов; тем не менее, использовать его один раз перед публикацией исходного кода – хорошая идея.
-rectypes
Разрешать произвольные рекурсивные типы во время проверки типов. По умолчанию поддерживаются только рекурсивные типы, где рекурсия проходит через тип объекта. Обратите внимание, что после создания интерфейса с использованием этого флага, вы должны использовать его снова для всех зависимостей.
-runtime-variant приставка
Добавить строку приставку к имени используемой программой библиотеки выполнения. В настоящее время поддерживается только одна такая приставка: d, и только если компилятор OCaml был сконфигурирован с опцией -with-debug-runtime. Эта приставка обеспечивает отладочную версию выполнения, которая полезна для отладки проблем с указателями в низкоуровневом коде, таком как C-стыки.
-safe-string
Обеспечить разделение между типами string и bytes, тем самым сделав строки неизменяемыми. Это значение по умолчанию и используется с OCaml 5.0.
-safer-matching
Не использовать информацию о типе для оптимизации сопоставления с образцом. Это позволяет обнаруживать ошибки сопоставления, даже если сопоставление по шаблону ошибочно предполагалось исчерпывающим. Это влияет только на компиляцию GADTs и полиморфных вариантов.
-short-paths
Когда тип виден по нескольким путям модулей, использовать самый короткий при печати имени типа в выведенных интерфейсах и сообщениях об ошибках и предупреждениях. Имена идентификаторов, начинающиеся с подчеркивания _ или содержащие двойные подчеркивания __, имеют штраф в +10 при вычислении их длины.
-stop-after этап
Остановить компиляцию после указанного этапа компиляции. В настоящее время поддерживаются следующие этапы: parsing, typing.
-strict-sequence
Принудительно устанавливать тип левой части каждой последовательности в единицу.
-strict-formats
Отклонять неверные форматы, которые принимались в реализациях форматов предыдущих версий. Вы должны использовать этот флаг для обнаружения и исправления таких неверных форматов, так как они будут отклоняться будущими версиями OCaml.
-unboxed-types
Если тип может быть разложен (т. е. запись с одним аргументом или конкретный тип данных с одним конструктором одного аргумента), он будет разложен, если не помечен как [@@ocaml.boxed].
-no-unboxed-types
Если тип может быть разложен, он будет упакован, если не помечен как [@@ocaml.unboxed]. Это значение по умолчанию.
-unsafe
Отключить проверку границ для доступа к массивам и строкам (конструкции v.(i) и s.[i]). Программы, скомпилированные с -unsafe, поэтому немного быстрее, но небезопасны: может произойти что угодно, если программа обращается к элементу массива или строки за пределами границ. Кроме того, отключить проверку на ноль делителя в операциях целочисленного деления и модуля. С -unsafe целочисленное деление (или модуль) на ноль может остановить программу или продолжить с неопределенным результатом вместо повышения исключения Division_by_zero.
-unsafe-string
Определить типы string и bytes, тем самым сделав строки изменяемыми. Это предназначено для совместимости со старым исходным кодом и не должно использоваться с новым программным обеспечением. Эта опция вызывает ошибку безоговорочно с OCaml 5.0.
-use-runtime имя-выполнения
Сгенерировать исполняемый файл байткода, который может быть выполнен в пользовательской системе выполнения имя-выполнения, созданной ранее с помощью ocamlc -make-runtime имя-выполнения. Для получения дополнительной информации см. раздел ‍22.1.6.
-v
Вывести номер версии компилятора и расположение каталога стандартной библиотеки, а затем выйти.
-verbose
Вывести все внешние команды перед их выполнением, в частности вызовы компилятора C и компоновщика в режиме -custom. Полезно для отладки проблем с библиотеками C.
-version или -vnum
Вывести номер версии компилятора в короткой форме (например, 3.11.0), а затем выйти.
-w список-предупреждений
Включить, отключить или пометить как критическое предупреждения, указанные в аргументе warning-list. Каждое предупреждение может быть включено или выключено, и каждое предупреждение может быть критическим или некритическим. Если предупреждение отключено, оно не отображается и никак не влияет на компиляцию (даже если оно критическое). Если предупреждение включено, оно нормально отображается компилятором всякий раз, когда исходный код его вызывает. Если оно включено и критическое, компилятор также остановится с ошибкой после его отображения.

Аргумент warning-list представляет собой последовательность спецификаторов предупреждений без разделителей между ними. Спецификатор предупреждения может быть одним из следующих:

+num
Включить предупреждение номер num.
-num
Отключить предупреждение номер num.
@num
Включить и пометить как критическое предупреждение номер num.
+num1..num2
Включить предупреждения в заданном диапазоне.
-num1..num2
Отключить предупреждения в заданном диапазоне.
@num1..num2
Включить и пометить как критическое предупреждения в заданном диапазоне.
+letter
Включить набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
-letter
Отключить набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
@letter
Включить и пометить как критическое набор предупреждений, соответствующих letter. Буква может быть заглавной или строчной.
uppercase-letter
Включить набор предупреждений, соответствующих uppercase-letter.
lowercase-letter
Отключить набор предупреждений, соответствующих lowercase-letter.

В качестве альтернативы, warning-list может указать одно предупреждение по его мнемоническому имени (см. ниже), следующим образом:

+name
Включить предупреждение name.
-name
Отключить предупреждение name.
@name
Включить и пометить как критическое предупреждение name.

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

1 comment-start
Подозрительная метка начала комментария.
2 comment-not-end
Подозрительная метка конца комментария.
3
Устаревший синоним для предупреждения ’устаревший’.
4 fragile-match
Ломкий поиск совпадений: сопоставление, которое останется полным, даже если к одному из типов вариантов, с которыми производится сопоставление, будут добавлены дополнительные конструкторы.
5 ignored-partial-application
Частично применённая функция: выражение, результат которого имеет тип функции и игнорируется.
6 labels-omitted
Метка опущена в применении функции.
7 method-override
Метод переопределён.
8 partial-match
Частичное соответствие: отсутствующие случаи в поиске совпадений по шаблонам.
9 missing-record-field-pattern
Отсутствующие поля в шаблоне записи.
10 non-unit-statement
Выражение в левой части последовательности, которое не имеет тип unit (и которое не является функцией, см. предупреждение номер 5).
11 redundant-case
Избыточный случай в поиске совпадений по шаблонам (неиспользуемый случай сопоставления).
12 redundant-subpat
Избыточный подшаблон в поиске совпадений по шаблонам.
13 instance-variable-override
Переменная экземпляра переопределена.
14 illegal-backslash
Некорректный обратный слэш в строковой константе.
15 implicit-public-methods
Приватный метод неявно сделан публичным.
16 unerasable-optional-argument
Неудаляемый необязательный аргумент.
17 undeclared-virtual-method
Необъявленный виртуальный метод.
18 not-principal
Неглавный тип.
19 non-principal-labels
Тип без главности.
20 ignored-extra-argument
Неиспользуемый аргумент функции.
21 nonreturning-statement
Невозвращаемое утверждение.
22 preprocessor
Предупреждение препроцессора.
23 useless-record-with
Бесполезная запись with -клаузы.
24 bad-module-name
Неправильное имя модуля: имя исходного файла не является допустимым именем модуля OCaml.
25
Игнорируется: теперь часть предупреждения 8.
26 unused-var
Подозрительная неиспользуемая переменная: неиспользуемая переменная, которая связана с let или as, и не начинается с символа подчёркивания (_).
27 unused-var-strict
Безобидная неиспользуемая переменная: неиспользуемая переменная, которая не связана с let или as, и не начинается с символа подчёркивания (_).
28 wildcard-arg-to-constant-constr
Шаблон со звёздочкой, заданный как аргумент константного конструктора.
29 eol-in-string
Неэкранированный символ конца строки в строковой константе (непереносимый код).
30 duplicate-definitions
Два тега или конструктора с одинаковым именем определены в двух взаимно рекурсивных типах.
31 module-linked-twice
Один и тот же модуль связан дважды в одном исполняемом файле.
I
Игнорируется: теперь является жёсткой ошибкой (начиная с 5.1).
32 unused-value-declaration
Неиспользуемое объявление значения. (начиная с 4.00)
33 unused-open
Неиспользуемое утверждение open. (начиная с 4.00)
34 unused-type-declaration
Неиспользуемое объявление типа. (начиная с 4.00)
35 unused-for-index
Неиспользуемый индекс цикла for. (начиная с 4.00)
36 unused-ancestor
Неиспользуемая переменная предка. (начиная с 4.00)
37 unused-constructor
Неиспользуемый конструктор. (начиная с 4.00)
38 unused-extension
Неиспользуемый конструктор расширения. (начиная с 4.00)
39 unused-rec-flag
Неиспользуемый флаг rec. (начиная с 4.00)
40 name-out-of-scope
Имя конструктора или метки используется вне области видимости. (начиная с 4.01)
41 ambiguous-name
Неоднозначное имя конструктора или метки. (начиная с 4.01)
42 disambiguated-name
Яснее определено имя конструктора или метки (предупреждение совместимости). (начиная с 4.01)
43 nonoptional-label
Необязательная метка применена как необязательная. (начиная с 4.01)
44 open-shadow-identifier
Утверждение open затеняет уже определённый идентификатор. (начиная с 4.01)
45 open-shadow-label-constructor
Утверждение open затеняет уже определённую метку или конструктор. (начиная с 4.01)
46 bad-env-variable
Ошибка в переменной среды. (начиная с 4.01)
47 attribute-payload
Неправильная нагрузка атрибута. (начиная с 4.02)
48 eliminated-optional-arguments
Неявное удаление необязательных аргументов. (начиная с 4.02)
49 no-cmi-file
Отсутствует файл cmi при поиске псевдонима модуля. (начиная с 4.02)
50 unexpected-docstring
Неожиданный комментарий к документации. (с версии 4.03)
51 wrong-tailcall-expectation
Вызов функции с неверным атрибутом @tailcall. (с версии 4.03)
52 fragile-literal-pattern (см. 13.5.3)
Хрупкий шаблон констант. (с версии 4.03)
53 misplaced-attribute
Атрибут не может быть использован в данном контексте. (с версии 4.03)
54 duplicated-attribute
Атрибут используется более одного раза в выражении. (с версии 4.03)
55 inlining-impossible
Внедрение невозможно. (с версии 4.03)
56 unreachable-case
Недостижимый случай в шаблоне сопоставления (на основе информации о типе). (с версии 4.03)
57 ambiguous-var-in-pattern-guard (см. 13.5.4)
Неоднозначные переменные в шаблоне «или» под условием. (с версии 4.03)
58 no-cmx-file
Отсутствует файл cmx. (с версии 4.03)
59 flambda-assignment-to-non-mutable-value
Присваивание неизменяемому значению. (с версии 4.03)
60 unused-module
Неиспользуемое объявление модуля. (с версии 4.04)
61 unboxable-type-in-prim-decl
Неразличимый тип в примитивном объявлении. (с версии 4.04)
62 constraint-on-gadt
Ограничение типа на объявлении типа GADT. (с версии 4.06)
63 erroneous-printed-signature
Неверная напечатанная сигнатура. (с версии 4.08)
64 unsafe-array-syntax-without-parsing
-unsafe используется с препроцессором, возвращающим синтаксическое дерево. (с версии 4.08)
65 redefining-unit
Объявление типа, определяющего новый конструктор ’()’. (с версии 4.08)
66 unused-open-bang
Неиспользуемое утверждение open!. (с версии 4.08)
67 unused-functor-parameter
Неиспользуемый параметр функтора. (с версии 4.10)
68 match-on-mutable-state-prevent-uncurry
Сопоставление с образцом, зависящее от изменяемого состояния, предотвращает разыменование оставшихся аргументов. (с версии 4.12)
69 unused-field
Неиспользуемое поле записи. (с версии 4.13)
70 missing-mli
Отсутствует файл интерфейса. (с версии 4.13)
71 unused-tmc-attribute
Неиспользуемый атрибут @tail_mod_cons. (с версии 4.14)
72 tmc-breaks-tailcall
Запись хвостового вызова преобразуется в нехвостовой вызов при преобразовании @tail_mod_cons. (с версии 4.14)
73 generative-application-expects-unit
Генеративный функтор применяется к пустой структуре (конец структуры), а не к (). (с версии 5.1)
A
все предупреждения
C
предупреждения 1, 2.
D
Псевдоним предупреждения 3.
E
Псевдоним предупреждения 4.
F
Псевдоним предупреждения 5.
K
предупреждения 32, 33, 34, 35, 36, 37, 38, 39.
L
Псевдоним предупреждения 6.
M
Псевдоним предупреждения 7.
P
Псевдоним предупреждения 8.
R
Псевдоним предупреждения 9.
S
Псевдоним предупреждения 10.
U
предупреждения 11, 12.
V
Псевдоним предупреждения 13.
X
предупреждения 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 30.
Y
Псевдоним предупреждения 26.
Z
Псевдоним предупреждения 27.

Значение по умолчанию — -w +a-4-6-7-9-27-29-32..42-44-45-48-50-60. Оно отображается с помощью ocamlc -help. Обратите внимание, что предупреждения 5 и 10 не всегда срабатывают, в зависимости от внутреннего устройства проверяющего типа.

-warn-error warning-list
Сделать предупреждения, указанные в аргументе warning-list, фатальными. Компилятор завершится с ошибкой, когда одно из этих предупреждений будет выведено. warning-list имеет тот же смысл, что и для параметра -w: знак + (или заглавная буква) отмечает соответствующие предупреждения как фатальные, знак - (или строчная буква) возвращает их к предупреждениям без фатальных последствий, а знак @ одновременно включает и отмечает соответствующие предупреждения как фатальные.

Примечание: в производственном коде не рекомендуется использовать наборы предупреждений (т. е. буквы) в качестве аргументов для -warn-error, поскольку это может нарушить сборку при добавлении новых предупреждений в будущие версии OCaml.

Значение по умолчанию — -warn-error -a (никакое предупреждение не является фатальным).

-warn-help
Показать описание всех доступных номеров предупреждений.
-where
Вывести расположение стандартной библиотеки, а затем выйти.
-with-runtime
Включить системный запуск в сгенерированной программе. Это значение по умолчанию.
-without-runtime
Компилятор не включает системный запуск (ни ссылку на него) в сгенерированную программу; его необходимо предоставить отдельно.
- file
Обработать file как имя файла, даже если оно начинается с символа тире (-).
-help or --help
Отобразить краткое резюме использования и выйти.
управление-cli-контекстом

Контекстное управление параметрами командной строки

Командная строка компилятора может быть изменена «извне» с помощью следующих механизмов. Они являются экспериментальными и могут быть изменены. Их следует использовать только для экспериментальной и опытной работы, а не в выпускаемых пакетах.

OCAMLPARAM (переменная окружения)
Набор аргументов, которые будут добавлены перед или после аргументов из командной строки. Аргументы задаются в виде списка name=value пар, разделённых запятыми. Знак _ используется для указания позиции аргументов командной строки, т.е. a=x,_,b=y означает, что a=x должно быть выполнено до обработки аргументов, а b=y — после. Наконец, альтернативный разделитель может быть указан как первый символ строки, из набора :|; ,.
ocaml_compiler_internal_params (файл в каталоге stdlib)
Сопоставление имён файлов с списками аргументов, которые будут добавлены к аргументам командной строки (и аргументам OCAMLPARAM).

3 Модули и файловая система

В этом кратком разделе описывается взаимосвязь между именами модулей, соответствующих единицам компиляции, и именами файлов, содержащих их скомпилированный интерфейс и скомпилированную реализацию.

Компилятор всегда вычисляет имя модуля, беря имя базового файла исходного кода (.ml или .mli файл) в верхнем регистре. То есть, он удаляет ведущее имя каталога, если таковое имеется, а также суффикс .ml или .mli, затем устанавливает первую букву в верхний регистр, чтобы удовлетворить требованию, что имена модулей должны быть в верхнем регистре. Например, компиляция файла mylib/misc.ml предоставляет реализацию для модуля с именем Misc. Другие единицы компиляции могут ссылаться на компоненты, определённые в mylib/misc.ml, под именами Misc.name; они также могут использовать open Misc, а затем использовать имена name без квалификатора.

Файлы .cmi и .cmo, созданные компилятором, имеют то же базовое имя, что и исходный файл. Следовательно, скомпилированные файлы всегда имеют своё базовое имя, равное (за исключением перевода первой буквы в верхний регистр) имени модуля, который они описывают (для файлов .cmi) или реализуют (для файлов .cmo).

Когда компилятор встречает ссылку на свободный идентификатор модуля Mod, он ищет в пути поиска файл с именем Mod.cmi или mod.cmi и загружает содержащийся в нём скомпилированный интерфейс. Вследствие этого, переименование файлов .cmi не рекомендуется: имя файла .cmi всегда должно соответствовать имени единицы компиляции, которую он реализует. Допустимо переместить их в другой каталог, если их базовое имя сохраняется, и компилятору заданы соответствующие параметры -I. Компилятор выдаст ошибку, если он загрузит файл .cmi, который был переименован.

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

4 Распространённые ошибки

В этом разделе описаны и объяснены наиболее часто встречающиеся сообщения об ошибках.

Файл filename не найден
Имя файла не найдено ни в текущем каталоге, ни в каталогах пути поиска. Файл filename — это либо скомпилированный интерфейсный файл (.cmi), либо скомпилированный байткодовый файл (.cmo). Если filename имеет формат mod.cmi, это значит, что вы пытаетесь скомпилировать файл, который ссылается на идентификаторы из модуля mod, но интерфейс модуля mod ещё не скомпилирован. Решение: сначала скомпилируйте mod.mli или mod.ml, чтобы создать скомпилированный интерфейс mod.cmi.

Если filename имеет формат mod.cmo, это значит, что вы пытаетесь связать байткодовый объектный файл, которого ещё нет. Решение: сначала скомпилируйте mod.ml.

Если ваша программа расположена в нескольких каталогах, эта ошибка также может появиться, потому что вы не указали каталоги для поиска. Решение: добавьте правильные опции -I в командную строку.

Поврежденный скомпилированный интерфейс filename
Компилятор выдает эту ошибку, когда пытается прочитать скомпилированный интерфейсный файл (.cmi), имеющий неправильную структуру. Это означает, что при создании файла .cmi произошла ошибка: диск был заполнен, компилятор был прерван в середине создания файла и т. д. Эта ошибка также может появиться, если файл .cmi был изменён после его создания компилятором. Решение: удалите повреждённый файл .cmi и перестройте его.
Это выражение имеет тип t1, но используется с типом t2
Это, пожалуй, самая распространенная ошибка типа в программах. Тип t1 — это тип, выведенный для выражения (часть программы, отображаемая в сообщении об ошибке), глядя на само выражение. Тип t2 — это тип, ожидаемый контекстом выражения; он выводится, глядя на то, как значение этого выражения используется в остальной части программы. Если два типа t1 и t2 не совместимы, то генерируется указанная выше ошибка.

В некоторых случаях сложно понять, почему два типа t1 и t2 несовместимы. Например, компилятор может сообщить, что «выражение типа foo не может использоваться с типом foo», и кажется, что оба типа foo совместимы. Это не всегда так. Два конструктора типов могут иметь одинаковое имя, но на самом деле представлять разные типы. Это может произойти, если конструктор типов переопределен. Пример:

        type foo = A | B
        let f = function A -> 0 | B -> 1
        type foo = C | D
        f C

Это приводит к сообщению об ошибке «выражение C типа foo не может использоваться с типом foo».

Тип этого выражения, t, содержит свободные переменные, которые не могут быть обобщены
Переменные типов ('a, 'b, …) в типе t могут находиться в одном из двух состояний: обобщенные (что означает, что тип t действителен для всех возможных экземпляров переменных) и не обобщенные (что означает, что тип t действителен только для одного экземпляра переменных). В связи let let name = expr, проверка типов обычно обобщает как можно больше переменных типов в типе expr. Однако это приводит к некорректности (хорошо типизированная программа может завершиться ошибкой) в сочетании с полиморфными изменяемыми структурами данных. Чтобы избежать этого, обобщение выполняется в связях let только в том случае, если связываемое выражение expr принадлежит классу «синтаксических значений», который включает константы, идентификаторы, функции, кортежи синтаксических значений и т. д. Во всех остальных случаях (например, expr является применением функции), могла быть создана полиморфная изменяемая структура, и поэтому обобщение отключено для всех переменных, встречающихся в контравариантных или невариантных ветвях типа. Например, если тип не значения равен 'a list , переменная обобщается (list — ковариантный конструктор типов), но не в 'a list -> 'a list (левая ветвь -> контравариантна) или 'a ref (ref невариантна).

Необобщенные переменные типов в типе не создают проблем внутри данной структуры или модуля компиляции (содержимое файла .ml или интерактивной сессии), но они не могут быть разрешены внутри сигнатур или в скомпилированных интерфейсах (.cmi файл), потому что они могут быть использованы несовместимо позже. Поэтому компилятор сигнализирует об ошибке, когда структура или модуль компиляции определяет значение name, тип которого содержит необобщенные переменные типов. Есть два способа исправить эту ошибку:

  • Добавьте ограничение типа или файл .mli, чтобы присвоить name мономорфный тип (без переменных типов). Например, вместо того, чтобы писать
        let sort_int_list = List.sort Stdlib.compare
        (* inferred type 'a list -> 'a list, with 'a not generalized *)
    
    напишите
        let sort_int_list = (List.sort Stdlib.compare : int list -> int list);;
    
  • Если вам действительно нужен полиморфный тип для name, преобразуйте определяющее выражение в функцию, добавив дополнительный параметр. Например, вместо того, чтобы писать
        let map_length = List.map Array.length
        (* inferred type 'a array list -> int list, with 'a not generalized *)
    
    напишите
        let map_length lv = List.map Array.length lv
    
Ссылка на неопределённый глобальный модуль mod
Эта ошибка появляется при попытке связать неполный или неправильно упорядоченный набор файлов. Либо вы забыли указать реализацию модуля mod в командной строке (как правило, файл mod.cmo, или библиотеку, содержащую этот файл). Решение: добавьте недостающий файл .ml или .cmo в командную строку. Или вы указали реализацию модуля mod, но она указана слишком поздно в командной строке: реализация модуля mod должна предшествовать всем байткодовым объектным файлам, которые ссылаются на модуль mod. Решение: измените порядок файлов .ml и .cmo в командной строке.

Конечно, вы всегда столкнётесь с этой ошибкой, если у вас есть взаимно рекурсивные функции между модулями. То есть, функция Mod1.f вызывает функцию Mod2.g, а функция Mod2.g вызывает функцию Mod1.f. В этом случае, какие бы перестановки вы ни выполнили в командной строке, программа будет отклонена на этапе компоновки. Решения:

  • Разместите f и g в одном модуле.
  • Параметризуйте одну функцию другой. То есть, вместо
    mod1.ml:    let f x = ... Mod2.g ...
    mod2.ml:    let g y = ... Mod1.f ...
    
    определите
    mod1.ml:    let f g x = ... g ...
    mod2.ml:    let rec g y = ... Mod1.f g ...
    
    и свяжите mod1.cmo до mod2.cmo.
  • Используйте ссылку для хранения одной из двух функций, как в :
    mod1.ml:    let forward_g =
                    ref((fun x -> failwith "forward_g") : <type>)
                let f x = ... !forward_g ...
    mod2.ml:    let g y = ... Mod1.f ...
                let _ = Mod1.forward_g := g
    
Внешняя функция f недоступна
Эта ошибка появляется при попытке связать код, который вызывает внешние функции, написанные на C. Как описано в главе ‍22, такой код должен быть связан с библиотеками C, которые реализуют необходимую функцию C f. Если используемые библиотеки C не являются динамическими библиотеками (DLL), код должен быть связан в режиме «пользовательской среды выполнения». Решение: добавьте необходимые библиотеки C в командную строку и, возможно, опцию -custom.

5 Справочник по предупреждениям

В этом разделе подробно описываются и объясняются некоторые предупреждения:

5.1 Предупреждение 6: Метка опущена в применении функции

OCaml поддерживает полные применения без меток labels-omitted: если функция имеет известную арность, все аргументы не имеют меток, и их количество соответствует количеству необязательных параметров, то метки игнорируются, а необязательные параметры сопоставляются в порядке их определения. Необязательные аргументы имеют значения по умолчанию.

let f ~x ~y = x + y
let test = f 2 3

> let test = f 2 3
>            ^
> Warning 6 [labels-omitted]: labels x, y were omitted in the application of this function.

Поддержка labels-omitted приложений была добавлена при добавлении меток в OCaml для упрощения поэтапного внедрения меток в базу кода. Однако это имеет недостаток, ослабляющий дисциплину меток: если вы используете метки для предотвращения случайной перестановки двух параметров одного типа, метки labels-omitted снова делают эту ошибку возможной.

Предупреждение 6 предупреждает о использовании labels-omitted приложений, чтобы отговорить от их использования. При добавлении меток это предупреждение по умолчанию не было включено, поэтому пользователи часто использовали labels-omitted приложения, часто этого не замечая.

Со временем стало стандартной практикой включать это предупреждение для предотвращения ошибок порядка аргументов. Сейчас предупреждение включено по умолчанию начиная с OCaml 4.13. Labels-omitted приложения больше не рекомендуются, но пользователи, желающие сохранить этот переходный стиль, могут отключить предупреждение явно.

5.2 Предупреждение 9: отсутствующие поля в шаблоне записи

При сопоставлении с шаблонами записей может быть полезно сопоставить только несколько полей записи. Пропуск полей можно сделать неявно или явно, завершив шаблон записи с ; _. Однако неявное пропускание полей противоречит проверкам полноты сопоставления с шаблонами. Включение предупреждения 9 ставит проверку полноты выше удобства неявного пропуска полей и будет выдавать предупреждение при неявном пропуске полей в шаблонах записей. В частности, это предупреждение может помочь обнаружить шаблоны записей, которые могут потребовать обновления после добавления новых полей к типу записи.

type 'a point = {x : 'a; y : 'a}
let dx { x } = x (* implicit field elision: trigger warning 9 *)
let dy { y; _ } = y (* explicit field elision: do not trigger warning 9 *)

5.3 Предупреждение 52: хрупкий шаблон константы

Некоторые конструкторы, такие как конструкторы исключений Failure и Invalid_argument, принимают в качестве параметра значение string, содержащее текстовое сообщение для пользователя.

Эти текстовые сообщения обычно не являются стабильными во времени: места вызова, строящие эти конструкторы, могут уточнить сообщение в будущей версии, чтобы сделать его более явным и т. д. Поэтому опасно сопоставлять по точному значению сообщения. Например, до OCaml 4.02, Array.iter2 вызывало исключение

  Invalid_argument "arrays must have the same length"

Начиная с версии 4.03, оно выдает более информативное сообщение

  Invalid_argument "Array.iter2: arrays must have the same length"

но это означает, что любой код вида

  try ...
  with Invalid_argument "arrays must have the same length" -> ...

теперь неработоспособен и может привести к необработанным исключениям.

Предупреждение 52 призвано предотвратить написание такого хрупкого кода в первую очередь. Оно не возникает при каждом сопоставлении со строковой литералом, а только в том случае, когда авторы библиотек выразили намерение изменить значение параметра конструктора в будущем, используя атрибут ocaml.warn_on_literal_pattern (см. раздел руководства об встроенных атрибутах в 12.12.1):

type t =
  | Foo of string [@ocaml.warn_on_literal_pattern]
  | Bar of string

let no_warning = function
  | Bar "specific value" -> 0
  | _ -> 1

let warning = function
  | Foo "specific value" -> 0
  | _ -> 1

Warning 52 [fragile-literal-pattern]: Code should not depend on the actual values of
this constructor's arguments. They are only for information
and may change in future versions. (see manual section 13.5.3)

В частности, все встроенные исключения со строковым аргументом имеют этот атрибут: Invalid_argument, Failure, Sys_error — все они будут выдавать это предупреждение, если вы будете сопоставлять конкретный строковый аргумент.

Кроме того, встроенные исключения со структурированным аргументом, включающим строку, также имеют установленный атрибут: Assert_failure и Match_failure будут выдавать предупреждение для шаблона, использующего строковую литерал для сопоставления первого элемента их кортежного аргумента.

Если ваш код выдает это предупреждение, вы не должны изменять способ проверки конкретной строки для предотвращения предупреждения (например, используя сравнение строк внутри правой части вместо шаблона литерала), поскольку ваш код останется хрупким. Вместо этого вы должны расширить область шаблона, сопоставляя все возможные значения.

let warning = function
  | Foo _ -> 0
  | _ -> 1

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

Например,

try (int_of_string count_str, bool_of_string choice_str) with
  | Failure "int_of_string" -> (0, true)
  | Failure "bool_of_string" -> (-1, false)

следует переписать на более атомарные тесты. Например, используя шаблоны исключений, описанные в разделе ‍11.6, можно записать:

match int_of_string count_str with
  | exception (Failure _) -> (0, true)
  | count ->
    begin match bool_of_string choice_str with
    | exception (Failure _) -> (-1, false)
    | choice -> (count, choice)
    end

Единственный случай, когда это преобразование невозможно, — если вызов данной функции может вызвать разные исключения с одинаковым конструктором, но разными строковыми значениями. В этом случае вам придется проверять конкретные строковые значения. Это небезопасный дизайн API, и от него следует отговаривать: лучше определить более точные конструкторы исключений, чем хранить полезную информацию в строках.

5.4 Предупреждение 57: неоднозначные переменные в шаблоне «или» под защитой

Семантика шаблонов «или» в OCaml определяется с левосторонним смещением: значение v соответствует шаблону p | q, если оно соответствует p или q, но если оно соответствует обоим, то среда, захваченная соответствием, — это среда, захваченная p, а не q.

Хотя это свойство обычно интуитивно понятно, есть по крайней мере один конкретный случай, где может ожидать другая семантика. Рассмотрим шаблон, за которым следует условие «когда»: | ‍p ‍when ‍g ‍-> ‍e, например:

     | ((Const x, _) | (_, Const x)) when is_neutral x -> branch

Семантика ясна: сопоставьте проверяемую величину с шаблоном, если она соответствует, проверьте условие, и если условие выполняется, возьмите ветвь. В частности, рассмотрим вход (Const ‍a, Const ‍b), где a не проходит тест is_neutral ‍a, а b проходит тест is_neutral ‍b. При левосторонней семантике приведенный выше фрагмент кода не выбирается для своего входного значения: при сопоставлении (Const ‍a, Const ‍b) с шаблоном «или» выполнение в левой ветви проходит успешно, она возвращает среду x ‍-> ‍a, и затем условие is_neutral ‍a проверяется и терпит неудачу, ветвь не выбирается.

Однако можно рассмотреть другую семантику, которая здесь может быть более естественной: любая пара, у которой одна сторона проходит тест, выберет ветвь. В соответствии с этой семантикой предыдущий фрагмент кода эквивалентен

     | (Const x, _) when is_neutral x -> branch
     | (_, Const x) when is_neutral x -> branch

Это не принятая OCaml семантика.

Предупреждение 57 посвящено этим запутанным случаям, где указанная левосторонняя семантика не эквивалентна недетерминированной семантике (любая ветвь может быть выбрана) по отношению к конкретному условию. Более точно, оно предупреждает, когда условие использует «неоднозначные» переменные, которые связаны с различными частями проверяемых величин с разных сторон шаблона «или».

« Расширения языкаСистема командной строки или REPL (ocaml) »
Авторские права © 2024 Institut National de Recherche en Informatique et en Automatique

© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/comp.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API