Практический ответ заключается в том, что рекурсивные функции должны повсеместно количественно определять свои локально абстрактные типы с помощью let rec f: type a. .... = fun ...
.
Точнее, ваш пример можно упростить до
module type T = sig type t end
let rec f (type a) (m: (module T with type t = a)) = f m
, которые дают то же самоеerror as yours:
Error: Это выражение имеет тип (модуль T с типом t = a), но ожидалось выражение типа 'a Конструктор типа a выйдет из области действия
Эта ошибка может быть исправлена с помощью явного полного количественного определения: это можно сделать с помощью сокращенной записи (для универсально количественно определенного локально абстрактного типа):
let rec f: type a. (module T with type t = a) -> 'never = fun m -> f m
Причина такого поведения заключается в том, чтолокально абстрактный тип не должен выходить за рамки функции, которая их представила.Например, этот код
let ext_store = ref None
let store x = ext_store := Some x
let f (type a) (x:a) = store x
должен явно потерпеть неудачу, поскольку он пытается сохранить значение типа a
, которое является бессмысленным типом вне тела f
.
Следовательно, значения с локально абстрактным типом могут использоваться только полиморфной функцией.Например, этот пример
let id x = x
let f (x:a) : a = id x
подходит, потому что id x
работает для любого x
.
. Тогда возникает проблема с функцией, подобной
let rec f (type a) (m: (module T with type t = a)) = f m
.что тип f
еще не обобщен внутри своего тела, потому что обобщение типа в ML происходит при определении let
.Поэтому исправление заключается в явном указании компилятору, что f
является полиморфным в своем аргументе:
let rec f: 'a. (module T with type t = 'a) -> 'never =
fun (type a) (m:(module T with type t = a)) -> f m
Здесь 'a. ...
- это универсальная квантификация, которая должна читать forall 'a. ...
.Эта первая строка сообщает компилятору, что функция f
является полиморфной в своем первом аргументе, тогда как вторая строка явно вводит локально абстрактный тип a
для уточнения типа упакованного модуля.Разделение этих двух объявлений довольно многословно, поэтому сокращенная запись объединяет оба:
let rec f: type a. (module T with type t = a) -> 'never = fun m -> f m