2

我一直在使用多态变体来处理结果类型的错误(取自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);
4

2 回答 2

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 errortype 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

表明边界被简单地忽略了。

我已经在几个方面达到了我的知识极限,但是我在你的问题中添加了,因为它有几个具有重要知识的追随者,他们有望将最后的部分放在一起。

于 2018-12-26T09:53:48.710 回答
0

您是否有理由需要将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 ]
于 2018-12-29T07:08:26.493 回答