Можно ли комментировать составленный полиморфный вариант типа?
Я использую полиморфные варианты для обработки ошибок с типами результатов (взяты из http://keleshev.com/composable-error-handling-in-ocaml), и это отлично подходит для исчерпывающей проверки. Недавно я столкнулся с необходимостью аннотировать тип результата в функторе, но не уверен, возможно ли это. Это фрагмент с некоторыми комментариями о том, что я пытаюсь сделать:
open Belt;
/* A quick example of what's working for us right now. This is fine and
the result error type is [> `Error1 | `Error2 ] */
let res = Result.flatMap(Result.Error(`Error1), _ => Result.Error(`Error2));
/* A really generic version of what what we're trying to do */
module type General = {
type t;
type error;
let res: Result.t(t, error);
};
module Make = (M: General) => {
let res = M.res;
};
module Specific1 =
Make({
type t = string;
type error = [ | `Specific1Error];
let res = Result.Error(`Specific1Error);
});
module Specific2 =
Make({
type t = int;
type error = [ | `Specific2Error];
let res = Result.Error(`Specific2Error);
});
/* This definitely doesn't compile because the two error types
aren't the same but wondering if anything above can be changed so it
understands the error type is [> `Specific1Error | `Specific2Error] */
let res = Result.flatMap(Specific1.res, _ => Specific2.res);
2 ответа
Это не полный ответ, но он предоставляет немного больше информации и одного возможного решения или обходного пути.
Можно получить последнюю строку для компиляции, добавив явное приведение к конкретному комбинированному типу:
let res =
Result.flatMap(
Specific1.res :> Result.t(string, [`Specific1Error | `Specific2Error]),
_ => (Specific2.res :> Result.t(int, [`Specific1Error | `Specific2Error])));
Здесь их обоих принуждают к одному и тому же типу, так что у нас все хорошо. Что касается того, почему это должно быть ясно, я понимаю, что это должно предотвратить случайные ошибки от неправильного набора конструкторов. Смотрите больше в этом ответе.
Если type error
определили нижнюю границу, мы не должны были бы быть явными, как продемонстрировано этим:
let error1 : Result.t(int, [> `Error1]) = Result.Error(`Error1);
let error2 : Result.t(int, [> `Error2]) = Result.Error(`Error2);
let res = Result.flatMap(error1, _ => error2);
Но поскольку полиморфные варианты верхней и нижней границ имеют неявную переменную типа, нам, по крайней мере, придется изменить type error
в type error('a)
, К сожалению, даже тогда я не уверен, как заставить подпись модуля совпадать с реализациями, так как, например, это:
type error('a) = [> | `Specific1Error] as 'a;
не удается с
Signature mismatch:
...
Type declarations do not match:
type 'a error = 'a constraint 'a = [> `Specific1Error ]
is not included in
type 'a error
Their constraints differ.
Также невозможно привести к полиморфному типу варианта с нижней границей, и я не уверен, почему это так:
let res =
Result.flatMap(
Specific1.res :> Result.t(string, [> `Specific1Error]),
_ => (Specific2.res :> Result.t(int, [> `Specific2Error])));
не удается с
This has type:
(int, [ `Specific2Error ]) Result.t
But somewhere wanted:
(int, [ `Specific1Error ]) Result.t
These two variant types have no intersection
что указывает на то, что границы просто игнорируются.
Я достиг пределов своих знаний здесь по нескольким направлениям, но я добавил ocaml к вашему вопросу, так как у него есть несколько последователей со значительными знаниями, которые, мы надеемся, могут объединить последние части.
Есть ли причина, по которой вам нужно error
тип запечатан внутри вашего General
подпись модуля? Похоже, вам нужно знать сумму всех этих вариантов ошибок, чтобы аннотировать окончательный вариант. res
Значение в любом случае, вы можете сделать следующее? (Пожалуйста, извините за перевод в стандартный синтаксис и идиомы OCaml.)
open Core
type error =
[ `Specific1Error
| `Specific2Error
]
module type General = sig
type t
val res : (t, error) Result.t
end
module Make (M: General) = struct
let res = M.res
end
module Specific1 =
Make (struct
type t = string
let res = Result.Error `Specific1Error
end)
module Specific2 =
Make (struct
type t = int
let res = Result.Error `Specific2Error
end)
(* This type expands to
* (int, [ `Specific1Error | `Specific2Error ]) result
*)
let res : ('a , error) Result.t =
let open Result.Monad_infix in
Specific1.res >>= (fun _ -> Specific2.res)
(* These errors will still compose with others down the line: *)
type error_plus = [error | `More_errors ]