假设我有一个如下功能:
query_server : Server.t -> string Or_error.t Deferred.t
然后我生成一个延迟查询列表:
let queries : string Or_error.t Deferred.t list = List.map servers ~f:query_server
如何获得第一个没有失败的查询的结果(否则会出现一些错误)。基本上,我喜欢这样的功能:
any_non_error : 'a Or_error.t Deferred.t list -> 'a Or_error.t
另外,我不确定如何以某种方式汇总错误。也许我的函数需要一个额外的参数,比如Error.t -> Error.t -> Error.t
,或者是否有一种标准的方法来组合错误?
答案 0 :(得分:1)
一种简单的方法是使用Deferred.List
包含提升到Async monad中的列表操作,基本上是Kleisli类别中的容器接口。我们将按顺序尝试每台服务器,直到第一台服务器准备就绪,例如
let first_non_error =
Deferred.List.find ~f:(fun s -> query_server s >>| Result.is_ok)
当然,它不是any_non_error
,因为处理是连续的。此外,我们正在丢失错误信息(尽管后者很容易修复)。
因此,为了使其平行,我们将采用以下策略。我们将有两个延迟计算,第一个将并行运行所有查询并等待所有准备就绪,第二个将在收到Ok结果后立即确定。如果第一个发生在最后一个之前,那么这意味着所有服务器都失败了。所以让我们试试:
let query_servers servers =
let a_success,got_success = Pipe.create () in
let all_errors = Deferred.List.map ~how:`Parallel servers ~f:(fun s ->
query_server s >>| function
| Error err as e -> e
| Ok x as ok -> Pipe.write_without_pushback x; ok) in
Deferred.any [
Deferred.any all_errors;
Pipe.read a_success >>= function
| `Ok x -> Ok x
| `Eof -> assert false
]