f# - 如何在 F# 中组合/组合计算表达式?
问题描述
这不是为了实际需要,而是为了尝试学习一些东西。
我正在使用 FSToolKit 的asyncResult表达式,它非常方便,我想知道是否有一种方法可以“组合”表达式,例如此处的 async 和 result,或者是否必须编写自定义表达式?
这是我使用 CloudFlare 将 ip 设置为子域的函数示例:
let setSubdomainToIpAsync zoneName url ip =
let decodeResult (r: CloudFlareResult<'a>) =
match r.Success with
| true -> Ok r.Result
| false -> Error r.Errors.[0].Message
let getZoneAsync (client: CloudFlareClient) =
asyncResult {
let! r = client.Zones.GetAsync()
let! d = decodeResult r
return!
match d |> Seq.filter (fun x -> x.Name = zoneName) |> Seq.toList with
| z::_ -> Ok z // take the first one
| _ -> Error $"zone '{zoneName}' not found"
}
let getRecordsAsync (client: CloudFlareClient) zoneId =
asyncResult {
let! r = client.Zones.DnsRecords.GetAsync(zoneId)
return! decodeResult r
}
let updateRecordAsync (client: CloudFlareClient) zoneId (records: DnsRecord seq) =
asyncResult {
return!
match records |> Seq.filter (fun x -> x.Name = url) |> Seq.toList with
| r::_ -> client.Zones.DnsRecords.UpdateAsync(zoneId, r.Id, ModifiedDnsRecord(Name = url, Content = ip, Type = DnsRecordType.A, Proxied = true))
| [] -> client.Zones.DnsRecords.AddAsync(zoneId, NewDnsRecord(Name = url, Content = ip, Proxied = true))
}
asyncResult {
use client = new CloudFlareClient(Credentials.CloudFlare.Email, Credentials.CloudFlare.Key)
let! zone = getZoneAsync client
let! records = getRecordsAsync client zone.Id
let! update = updateRecordAsync client zone.Id records
return! decodeResult update
}
它与一个 C# 库接口,该库处理对 CloudFlare API 的所有调用并返回一个 CloudFlareResult 对象,该对象具有成功标志、结果和错误。
我将该类型重新映射到 Result<'a, string> 类型:
let decodeResult (r: CloudFlareResult<'a>) =
match r.Success with
| true -> Ok r.Result
| false -> Error r.Errors.[0].Message
我可以为它写一个表达式(假设因为我一直在使用它们,但还没有写我自己的),但是如果这有意义的话,我会很高兴有一个 asyncCloudFlareResult 表达式,甚至是一个 asyncCloudFlareResultOrResult 表达式。
我想知道是否有一种将表达式组合在一起的机制,就像 FSToolKit 一样(尽管我怀疑它只是那里的自定义代码)。
同样,这是一个学习问题,而不是实用性,因为它可能会添加比其价值更多的代码。
在 Gus 的评论之后,我意识到用一些更简单的代码来说明这一点会很好:
function DoA : int -> Async<AWSCallResult<int, string>>
function DoB : int -> Async<Result<int, string>>
AWSCallResultAndResult {
let! a = DoA 3
let! b = DoB a
return b
}
在这个例子中,我最终会得到两种类型,它们可以接受一个 int 并返回一个错误字符串,但它们是不同的。两者都有自己的表情,所以我可以根据需要链接它们。最初的问题是关于如何将这些组合在一起。
解决方案
可以通过重载扩展 CE。
下面的示例可以将CustomResult
类型与通常的结果构建器一起使用。
open FsToolkit.ErrorHandling
type CustomResult<'T, 'TError> =
{ IsError: bool
Error: 'TError
Value: 'T }
type ResultBuilder with
member inline _.Source(result : CustomResult<'T, 'TError>) =
if result.IsError then
Error result.Error
else
Ok result.Value
let computeA () = Ok 42
let computeB () = Ok 23
let computeC () =
{ CustomResult.Error = "oops. This went wrong"
CustomResult.IsError = true
CustomResult.Value = 64 }
let computedResult =
result {
let! a = computeA ()
let! b = computeB ()
let! c = computeC ()
return a + b + c
}
推荐阅读
- ios - Cordova 插件,带有原生故事板 UI
- python - 重复随机抽样;样本中位数的抽样分布
- file - 在 Dafny 中读取(写入)文件
- r - 错误:“df = read.csv(("/home/rstudio/IMDB_data.csv",”中的意外','
- java - Java Swing 中的 Word Wrap 与 Hard Wrap 像素限制
- parsing - Decoding a proprietary HEVC/MP4 stream
- php - laravel 5.7 登录程序问题
- css - 顶部带有@import 的全局scss 变量
- ios - 如何从嵌入在 UIScrollView 中的 UiView 中的 CollectionView 接收触摸事件
- c - 在 C 中将字符串文字作为参数传递时的分段错误