Spec-Zone.ru › OCaml 5.0

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

  • 13.1 Обзор компилятора
  • 13.2 Опции
  • 13.3 Модули и файловая система
  • 13.4 Распространенные ошибки
  • 13.5 Справочник по предупреждениям

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

13.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 ниже). Объектные файлы, содержащиеся в библиотеке, связываются как обычные .cmo-файлы (см. выше), в порядке, указанном при создании файла .cma. Единственное отличие состоит в том, что если объектный файл, содержащийся в библиотеке, нигде не упоминается в программе, он не подключается к ней.
  • Аргументы, заканчивающиеся на .c, передаются компилятору C, который генерирует объектный файл .o ( .obj в Windows). Этот объектный файл связывается с программой, если установлена опция -custom (см. описание опции -custom ниже).
  • Аргументы, заканчивающиеся на .o или .a ( .obj или .lib в Windows) предполагаются объектами и библиотеками файлов C. Они передаются C-линковщику при линковке в режиме -custom (см. описание опции -custom ниже).
  • Аргументы, заканчивающиеся на .so ( .dll в Windows) предполагаются динамическими библиотеками C (DLL). При линковке они проверяются на наличие внешних функций C, на которые ссылается код OCaml, и их имена записываются в сгенерированный байткодовый исполняемый файл. Система выполнения ocamlrun затем динамически загружает их при запуске программы.

Выходной файл этапа линковки содержит скомпилированный байт-код, который может быть выполнен интерпретатором байт-кода OCaml: командой ocamlrun. Если a.out — имя файла, созданного этапом линковки, команда

        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 обычно полезны для инструментов инспекции кода.

13.2 Опции

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

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

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

-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.
-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-подобных shared library dlllibname.so (или dlllibname.dll в Windows) системой времени выполнения ocamlrun при запуске программы.
-dllpath dir
Добавляет директорию dir в путь поиска динамических C-библиотек во время выполнения. Во время линковки, 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).
-i
Принудить компилятор печатать все определённые имена (с их выведенными типами или определениями) во время компиляции реализации (.ml файл). Никаких скомпилированных файлов (.cmo и .cmi файлы) не генерируются. Это может быть полезно для проверки типов, выведенных компилятором. Кроме того, поскольку вывод соответствует синтаксису интерфейсов, это может помочь в написании явного интерфейса (.mli файл) для файла: просто перенаправьте стандартный вывод компилятора в .mli файл и отредактируйте его, удалив все объявления неэкспортированных имён.
-I directory
Добавить указанную директорию в список директорий, которые будут просматриваться при поиске скомпилированных файлов интерфейсов (.cmi), скомпилированных файлов объектного кода .cmo, библиотек (.cma) и C-библиотек, указанных с помощью -cclib -lxxx. По умолчанию, текущая директория ищется первой, затем стандартная директория библиотеки. Директории, добавленные с помощью -I, ищутся после текущей директории в порядке их указания в командной строке, но перед стандартной директорией библиотеки. См. также параметр -nostdlib.

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

-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 имя_выходного_файла
Укажите имя выходного файла. Для исполняемых файлов по умолчанию выходное имя — 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 Модуль
Открывает указанный модуль перед обработкой файлов интерфейса или реализации. Если задано несколько опций -open, они обрабатываются в порядке следования, точно так же, как если бы операторы open! Модуль1;; ... open! МодульN;; были добавлены в начало каждого файла.
-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 команда
Заставляет компилятор вызвать данную команду в качестве препроцессора для каждого исходного файла. Вывод команды перенаправляется в промежуточный файл, который затем компилируется. Если ошибок компиляции нет, промежуточный файл удаляется после этого.
-ppx команда
После парсинга, передаёт абстрактное синтаксическое дерево в препроцессор команда. Модуль Ast_mapper, описанный в главе 29: Ast_mapper , реализует внешний интерфейс препроцессора.
-principal
Проверяет пути информации во время проверки типов, чтобы убедиться, что все типы получены главным образом. При использовании помеченных аргументов и/или полиморфных методов этот флаг необходим, чтобы гарантировать, что будущие версии компилятора смогут правильно выводить типы, даже если внутренние алгоритмы изменятся. Все программы, принятые в режиме -principal, также принимаются в режиме по умолчанию с эквивалентными типами, но разными двоичными сигнатурами, и это может замедлить проверку типов; тем не менее, это хорошая идея использовать его один раз перед публикацией исходного кода.
-rectypes
Разрешает произвольные рекурсивные типы во время проверки типов. По умолчанию поддерживаются только рекурсивные типы, где рекурсия проходит через тип объекта. Обратите внимание, что после создания интерфейса с помощью этого флага, вы должны использовать его снова для всех зависимостей.
-runtime-variant суффикс
Добавляет строку суффикс к имени библиотеки времени выполнения, используемой программой. В настоящее время поддерживается только один такой суффикс: d, и только если компилятор OCaml был сконфигурирован с опцией -with-debug-runtime. Этот суффикс предоставляет отладочную версию времени выполнения, которая полезна для отладки проблем указателей в низкоуровневом коде, таком как C-заглушки.
-stop-after этап
Останавливает компиляцию после указанного этапа компиляции. В настоящее время поддерживаются следующие этапы: parsing, typing.
-safe-string
Обеспечьте разделение типов string и bytes, тем самым сделав строки неизменяемыми. Это значение по умолчанию, и оно применяется с OCaml 5.0.
-short-paths
Когда тип виден под несколькими путями модулей, используйте кратчайший путь при печати имени типа в выведенных интерфейсах, сообщениях об ошибках и предупреждениях. Имена идентификаторов, начинающиеся с подчеркивания _ или содержащие двойные подчеркивания __, увеличивают длину на 10 единиц при вычислении длины.
-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 runtime-name
Сгенерировать исполняемый файл байткода, который может выполняться в пользовательской системе выполнения runtime-name, созданной ранее с помощью ocamlc -make-runtime runtime-name. Дополнительную информацию см. в разделе 22.1.6.
-v
Вывести номер версии компилятора и расположение каталога стандартной библиотеки, затем завершиться.
-verbose
Вывести все внешние команды перед их выполнением, в частности вызовы компилятора и компоновщика C в режиме -custom. Полезно для отладки проблем с библиотеками C.
-version или -vnum
Вывести номер версии компилятора в короткой форме (например, 3.11.0), затем завершиться.
-w warning-list
Включить, отключить или пометить как фатальные предупреждения, указанные в аргументе 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
Модуль подключается дважды в одном исполняемом файле. (с версии 4.00)
32 unused-value-declaration
Неиспользуемое объявление значения. (с версии 4.00)
33 unused-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)
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+31 (только предупреждение 31 является фатальным).

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

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

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

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

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

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

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

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

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

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

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

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

END_OF_DOCUMENT_MARKER
Файл 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 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.

13.5 Справочная информация по предупреждениям

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

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

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

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 больше не рекомендуются, но пользователи, желающие сохранить этот переходный стиль, могут отключить предупреждение явно.

13.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 *)

13.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)

В частности, все встроенные исключения со строковым аргументом имеют этот атрибут: 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 небезопасен и следует избегать: лучше определить более точные конструкторы исключений, чем хранить полезную информацию в строках.

13.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 предназначено для этих запутанных случаев, где указанная левосторонняя семантика не эквивалентна недетерминированной семантике (любая ветвь может быть выбрана) относительно конкретного условия. Более точно, оно выводится, когда условие использует «неоднозначные» переменные, которые связаны с разными частями анализируемых значений разными сторонами шаблона «или».

© 1995-2022 INRIA.
https://v2.ocaml.org/releases/5.0/htmlman/comp.html

Spec-Zone.ru

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