F# 在F到链计算中有什么方法吗?

F# 在F到链计算中有什么方法吗?,f#,F#,我想创建一个表达式链,当计算停止时,其中任何一个都可能失败 对于Unix管道,通常是这样的: bash-3.2$ echo && { echo 'a ok'; echo; } && { echo 'b ok'; echo; } a ok b ok 出现故障时,管道停止运行: echo && { echo 'a ok'; false; } && { echo 'b ok'; echo; } a ok 我可以处理选项,但我的

我想创建一个表达式链,当计算停止时,其中任何一个都可能失败

对于Unix管道,通常是这样的:

bash-3.2$ echo && { echo 'a ok'; echo; }  && { echo 'b ok'; echo; }
a ok

b ok
出现故障时,管道停止运行:

echo && { echo 'a ok'; false; }  && { echo 'b ok'; echo; }

a ok
我可以处理选项,但我的问题是,我可能希望在每个分支中执行多个操作:

let someExternalOperation = callToAnAPI()
match someExternalOperation with
| None -> LogAndStop()
| Some x -> LogAndContinue()
然后,我希望继续进行其他API调用,并且仅在出现错误时停止

F中有类似的东西吗

更新1:

我试图做的是调用外部API。每次呼叫都可能失败。最好尝试重试,但不是必需的。

您可以同时使用F Async和Result类型来表示每个API调用的结果。然后,您可以使用这些类型的绑定函数来构建一个工作流,在该工作流中,您只能在前面的调用成功时继续处理。为了简化这一过程,您可以将每个api调用使用的异步封装为自己的类型,并围绕绑定这些结果构建一个模块,以协调链式计算。下面是一个简单的例子,可以说明这是什么样子:

首先,我们将设置ApiCallResult类型以包装Async和Result,并定义ApiCallError以表示HTTP错误响应或异常:

开放系统 开放系统.Net 开放系统.Net.Http 类型ApiCallError= |int*字符串的HttpError |exn的意外错误 键入ApiCallResult> 接下来,我们将创建一个模块来处理ApiCallResult实例,允许我们执行绑定、映射和返回等操作,以便我们可以处理计算结果并将其输入到下一个计算中

ApiCall模块= 让``return``x:ApiCallResult= 异步{返回Ok x} 让私有零:ApiCallResult= ``返回“`[] 让我们绑定f:'a->ApiCallResult:ApiCallResult acc:ApiCallResult= acc |>绑定乐趣值->cur |>映射乐趣值->值::值 让我们加入结果= 结果|>顺序折叠组合零 然后,您将有一个模块来简单地执行API调用,然而这在实际场景中是有效的。这里有一个只处理带有查询参数的GET,但您可以使其更复杂:

模块Api= 让我们调用baseUrl:Uri queryString:string:ApiCallResult= 异步的{ 尝试 使用client=newhttpclient 让url= 让builder=UriBuilderbaseUrl builder.Query Async.task 让!content=response.content.ReadAsStringAsync |>Async.waittask 如果是response.issucessStatusCode,则 让!content=response.content.ReadAsStringAsync |>Async.waittask 返回Ok内容 其他的 返回错误int,content 使用ex-> 返回错误WebUtility.UrlEncode |>sprintf%s=%s name 最后,您将拥有实际的业务工作流逻辑,在该逻辑中,您调用多个API并将一个API的结果反馈给另一个API。在下面的示例中,无论您在何处看到callMathApi,它都会调用可能失败的外部REST API,并且通过使用ApiCall模块绑定API调用的结果,它只会进入下一个API如果上一次调用成功,则调用。您可以声明一个类似>>=的运算符,以在将计算绑定在一起时消除代码中的一些干扰:

模块MathWorkflow= 让private>>=xf=ApiCall.bind fx 让私有apiUrl=Urihttp://api.mathjs.org/v4/ //数学表达式的RESTAPI 让私有callMathApi表达式= 表达式|>Api.getQueryParam expr |>Api.call apiUrl 让平均值= 价值观 |>List.map sprintf%d |>String.concat+ |>callMathApi >>=有趣的总和-> sprintf%s/%d个总和值。长度 |>callMathApi 设平方平均值= 价值观 |>List.map乐趣值->sprintf%d*%d值 |>List.map callMathApi |>加入 |>ApiCall.map List.map int >>=平均值 本例使用Mathjs.org API计算整数列表的平均值,一个API调用计算总和,另一个API调用除以元素数,还允许您计算值列表平方的平均值,方法是异步调用API对列表中的每个元素进行平方,然后加入rE结果并计算平均值。您可以按如下方式使用这些函数我在实际API调用中添加了printfn,以便它记录HTTP请求:

呼叫平均值:

MathWorkflow.average[1;2;3;4;5]|>Async.RunSynchronously 产出:

Calling API: http://api.mathjs.org/v4/?expr=1%2B2%2B3%2B4%2B5
Calling API: http://api.mathjs.org/v4/?expr=15%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "3"
Calling API: http://api.mathjs.org/v4/?expr=2*2
Calling API: http://api.mathjs.org/v4/?expr=4*4
Calling API: http://api.mathjs.org/v4/?expr=6*6
Calling API: http://api.mathjs.org/v4/?expr=8*8
Calling API: http://api.mathjs.org/v4/?expr=10*10
Calling API: http://api.mathjs.org/v4/?expr=100%2B64%2B36%2B16%2B4
Calling API: http://api.mathjs.org/v4/?expr=220%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "44"
产出:

Calling API: http://api.mathjs.org/v4/?expr=1%2B2%2B3%2B4%2B5
Calling API: http://api.mathjs.org/v4/?expr=15%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "3"
Calling API: http://api.mathjs.org/v4/?expr=2*2
Calling API: http://api.mathjs.org/v4/?expr=4*4
Calling API: http://api.mathjs.org/v4/?expr=6*6
Calling API: http://api.mathjs.org/v4/?expr=8*8
Calling API: http://api.mathjs.org/v4/?expr=10*10
Calling API: http://api.mathjs.org/v4/?expr=100%2B64%2B36%2B16%2B4
Calling API: http://api.mathjs.org/v4/?expr=220%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "44"
使用ApiCallBuilder,您可以像这样重写MathWorkflow模块中的函数,使它们更易于阅读和编写:

让平均值= 美联社 伊卡尔{ 让我来!求和= 价值观 |>List.map sprintf%d |>String.concat+ |>callMathApi 回来 sprintf%s/%d个总和值。长度 |>callMathApi } 设平方平均值= 阿皮卡尔{ 让!平方= 价值观 |>List.map乐趣值->sprintf%d*%d值 |>List.map callMathApi |>加入 return!squares |>List.map int |>average } 正如您在问题中所描述的那样,每个API调用都是独立进行的,结果会反馈到下一个调用中,但是如果一个调用失败,则停止计算并返回错误。例如,如果将此处示例调用中使用的URL更改为v3 API,而不更改任何其他内容,则会得到以下结果:

Calling API: http://api.mathjs.org/v3/?expr=2*2
[<Struct>]
val it : Result<string,ApiCallError> =
  Error
    (HttpError
       (404,
        "<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /v3/</pre>
</body>
</html>
"))
您可以同时使用F Async和Result类型来表示每个API调用的结果。然后,您可以使用这些类型的绑定函数来构建一个工作流,在该工作流中,您只能在前面的调用成功时继续处理。为了简化这一过程,您可以将每个api调用使用的异步封装为自己的类型,并围绕绑定这些结果构建一个模块,以协调链式计算。下面是一个简单的例子,可以说明这是什么样子:

首先,我们将设置ApiCallResult类型以包装Async和Result,并定义ApiCallError以表示HTTP错误响应或异常:

开放系统 开放系统.Net 开放系统.Net.Http 类型ApiCallError= |int*字符串的HttpError |exn的意外错误 键入ApiCallResult> 接下来,我们将创建一个模块来处理ApiCallResult实例,允许我们执行绑定、映射和返回等操作,以便我们可以处理计算结果并将其输入到下一个计算中

ApiCall模块= 让``return``x:ApiCallResult= 异步{返回Ok x} 让私有零:ApiCallResult= ``返回“`[] 让我们绑定f:'a->ApiCallResult:ApiCallResult acc:ApiCallResult= acc |>绑定乐趣值->cur |>映射乐趣值->值::值 让我们加入结果= 结果|>顺序折叠组合零 然后,您将有一个模块来简单地执行API调用,然而这在实际场景中是有效的。这里有一个只处理带有查询参数的GET,但您可以使其更复杂:

模块Api= 让我们调用baseUrl:Uri queryString:string:ApiCallResult= 异步的{ 尝试 使用client=newhttpclient 让url= 让builder=UriBuilderbaseUrl builder.Query Async.task 让!content=response.content.ReadAsStringAsync |>Async.waittask 如果是response.issucessStatusCode,则 让!content=response.content.ReadAsStringAsync |>Async.waittask 返回Ok内容 其他的 返回错误int,content 使用ex-> 返回错误WebUtility.UrlEncode |>sprintf%s=%s name 最后,您将拥有实际的业务工作流逻辑,在该逻辑中,您调用多个API并将一个API的结果反馈给另一个API。在下面的示例中,无论您在何处看到callMathApi,它都会调用可能失败的外部REST API,并且通过使用ApiCall模块绑定API调用的结果,它只会进入下一个API如果上一次调用成功,则调用。您可以声明一个类似>>=的运算符,以在将计算绑定在一起时消除代码中的一些干扰:

模块MathWorkflow= 让private>>=xf=ApiCall.bind fx 让私有apiUrl=Urihttp://api.mathjs.org/v4/ //数学表达式的RESTAPI 让私有callMathApi表达式= 表达式|>Api.getQueryParam expr |>Api.call apiUrl 让平均值= 价值观 |>List.map sprintf%d |>String.concat+ |>callMathApi >>=有趣的总和-> sprintf%s/%d个总和值。长度 |>callMathApi 设平方平均值= 价值观 |>List.map乐趣值->sprintf%d*%d值 |>List.map callMathApi |>加入 |>ApiCall.map List.map int >>=平均值 本例使用Mathjs.org API计算整数列表的平均值,一个API调用计算总和,另一个API调用除以元素数,还允许您计算值列表平方的平均值,方法是异步调用API对列表中的每个元素进行平方,然后加入rE结果并计算平均值。您可以按如下方式使用这些函数我在实际API调用中添加了printfn,以便它记录HTTP请求:

打电话给艾弗拉 通用电气:

MathWorkflow.average[1;2;3;4;5]|>Async.RunSynchronously 产出:

Calling API: http://api.mathjs.org/v4/?expr=1%2B2%2B3%2B4%2B5
Calling API: http://api.mathjs.org/v4/?expr=15%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "3"
Calling API: http://api.mathjs.org/v4/?expr=2*2
Calling API: http://api.mathjs.org/v4/?expr=4*4
Calling API: http://api.mathjs.org/v4/?expr=6*6
Calling API: http://api.mathjs.org/v4/?expr=8*8
Calling API: http://api.mathjs.org/v4/?expr=10*10
Calling API: http://api.mathjs.org/v4/?expr=100%2B64%2B36%2B16%2B4
Calling API: http://api.mathjs.org/v4/?expr=220%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "44"
产出:

Calling API: http://api.mathjs.org/v4/?expr=1%2B2%2B3%2B4%2B5
Calling API: http://api.mathjs.org/v4/?expr=15%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "3"
Calling API: http://api.mathjs.org/v4/?expr=2*2
Calling API: http://api.mathjs.org/v4/?expr=4*4
Calling API: http://api.mathjs.org/v4/?expr=6*6
Calling API: http://api.mathjs.org/v4/?expr=8*8
Calling API: http://api.mathjs.org/v4/?expr=10*10
Calling API: http://api.mathjs.org/v4/?expr=100%2B64%2B36%2B16%2B4
Calling API: http://api.mathjs.org/v4/?expr=220%2F5
[<Struct>]
val it : Result<string,ApiCallError> = Ok "44"
使用ApiCallBuilder,您可以像这样重写MathWorkflow模块中的函数,使它们更易于阅读和编写:

让平均值= 阿皮卡尔{ 让我来!求和= 价值观 |>List.map sprintf%d |>String.concat+ |>callMathApi 回来 sprintf%s/%d个总和值。长度 |>callMathApi } 设平方平均值= 阿皮卡尔{ 让!平方= 价值观 |>List.map乐趣值->sprintf%d*%d值 |>List.map callMathApi |>加入 return!squares |>List.map int |>average } 正如您在问题中所描述的那样,每个API调用都是独立进行的,结果会反馈到下一个调用中,但是如果一个调用失败,则停止计算并返回错误。例如,如果将此处示例调用中使用的URL更改为v3 API,而不更改任何其他内容,则会得到以下结果:

Calling API: http://api.mathjs.org/v3/?expr=2*2
[<Struct>]
val it : Result<string,ApiCallError> =
  Error
    (HttpError
       (404,
        "<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /v3/</pre>
</body>
</html>
"))

有两种选择——但最好的选择取决于您所处理的实际情况。我认为抽象地回答这个问题可能会让你陷入一个糟糕的方向,因此最好能更多地了解你的具体情况。我已经更新了这个问题。我想我可以使用Fsharpx或者{}来实现这一点。使用F的类型系统来防止无效或其他坏数据污染您的管道是您想要实现的吗?或者,更确切地说,你为什么要选择F?我会选择F,这样我对结果的质量有信心。我将从获取API结果开始,并使用活动模式之类的东西将它们与DU值相匹配。但这在很大程度上取决于您希望看到的数据类型,这就是为什么我们需要更多信息来帮助回答您的问题。如果你喜欢这个想法,这里的人可能会写一个总结作为答案。有两个选择——但最好的选择取决于你所处理的实际情况。我认为抽象地回答这个问题可能会让你陷入一个糟糕的方向,因此最好能更多地了解你的具体情况。我已经更新了这个问题。我想我可以使用Fsharpx或者{}来实现这一点。使用F的类型系统来防止无效或其他坏数据污染您的管道是您想要实现的吗?或者,更确切地说,你为什么要选择F?我会选择F,这样我对结果的质量有信心。我将从获取API结果开始,并使用活动模式之类的东西将它们与DU值相匹配。但这在很大程度上取决于您希望看到的数据类型,这就是为什么我们需要更多信息来帮助回答您的问题。如果你喜欢这个想法,这里可能会有人写一个摘要作为答案。