我有一个DU (不要担心特定的类型,但是有两个普通选项,或者一些错误)
type Content =
| Episode of EpisodeJSON.Root
| Program of string
| Errors of List<exn>我有两个函数(同样,细节并不重要,只是类型):
let getEpisode : _ -> _ -> Async<Content> = ...
let getProgram : _ -> _ -> Async<Content> = ...我想写
let getContent : _ -> _ -> Async<Content> =
fun (contentBAPIClient: ContentBAPI.Client) id -> ...这样,它尝试从getEpisode获取数据,如果有错误,尝试从getProgram获取数据,如果再次出现错误,则将两个错误作为错误DU返回。
所以,这是可行的
let getContent : _ -> _ -> Async<Content> =
fun (contentBAPIClient: ContentBAPI.Client) id ->
async {
let! episodeMaybe = getEpisode contentBAPIClient id
let! programMaybe = getProgram contentBAPIClient id
return
match episodeMaybe with
| Errors xs ->
match programMaybe with
| Errors ys ->
Errors (List.append xs ys)
| program ->
program
| episode ->
episode
}但是我注意到即使数据是通过getepisode找到的,getprogram也在执行。
如何构造这个简单的函数来先尝试getEpisode,然后只在插曲“失败”时尝试getProgram
这个很好,还是有点笨重。
let getContent : _ -> _ -> Async<Content> =
fun (contentBAPIClient: ContentBAPI.Client) id ->
async {
let! episodeMaybe = getEpisode contentBAPIClient id
match episodeMaybe with
| Errors xs ->
let! programMaybe = getProgram contentBAPIClient id
match programMaybe with
| Errors ys ->
return Errors (List.append xs ys)
| program ->
return program
| episode ->
return episode
}发布于 2022-07-31 19:47:32
如果您使用某些库来处理表示可能失败的异步计算的值,即类型Async<Result<'TOk, 'TErrror>>,这将变得容易得多。
一个不错的选择应该是类似于FsToolkit.ErrorHandling的东西。这定义了asyncResult计算表达式以及一些您可能认为有用的原语。
为了让它编译,我从以下几个方面开始:
#r "nuget: FsToolkit.ErrorHandling"
open FsToolkit.ErrorHandling
type Client = class end
let getEpisode contentBAPIClient id =
async { return Error ["no episode"] }
let getProgram contentBAPIClient id =
async { return Ok "fine" }现在,要做(我认为)你正在做的事情,你可以使用:
let getContent (contentBAPIClient: Client) id =
getEpisode contentBAPIClient id
|> AsyncResult.orElseWith (fun e1 ->
getProgram contentBAPIClient id
|> AsyncResult.mapError (fun e2 -> e1 @ e2) )在这里,我们试图返回插曲,但如果没有,我们尝试获得程序(使用orElseWith)。唯一棘手的是,我们需要传递来自第一个调用的错误,然后使用mapError将这些错误附加到第二个调用的(潜在)错误中。
请注意,您还可以编写:
let getContent (contentBAPIClient: Client) id =
asyncResult {
let! ep = getEpisode contentBAPIClient id
and! prog = getProgram contentBAPIClient id
return ep, prog }这只有在两个调用都成功的情况下才能成功,因此它在许多场景中可能非常有用,但并不能完全完成您想要做的事情。
https://stackoverflow.com/questions/73165971
复制相似问题