2017-07-22 58 views
1

假設我有一個功能,如:得到任何非錯誤元素的遞延OCaml中/異步

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還是有標準的方法來組合錯誤?

回答

1

一個簡單的方法是使用Deferred.List,其中包含列表操作提升到異步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 
    ]