将结果列表转换为计算表达式中列表的结果?
Turn list of Result into Result of list inside a computation expression?
我有一个 Result<'T, 'E> list
,我想按照这些规则将其变成一个 Result<'T list, 'E>
:
- 如果任何
Result
是 Error
那么结果应该是 Error
- 如果结果是
Error
,它应该是列表中的第一个 Error
- 如果每个结果都是
OK
那么结果应该是 Ok
并且列表顺序应该保持
所以我按如下方式实现了这一点:
let all xs =
let folder = fun state next ->
match (state, next) with
| (Result.Ok ys, Result.Ok y) -> ys |> List.append [ y ] |> Result.Ok
| (Result.Error e, _) -> Result.Error e
| (_, Result.Error e) -> Result.Error e
Seq.fold folder (Result.Ok []) xs
但是,这似乎已经在标准库中实现了。有吗?
其次,我有一个 Result
的计算表达式,如下所示:
type ResultBuilder () =
member this.Bind(x, f) =
match x with
| Result.Ok o -> f o
| Result.Error e -> Result.Error e
member this.Return(value) = Result.Ok value
member this.ReturnFrom(value) = value
let result = new ResultBuilder()
我可以在 result { ... }
中使用 all
但是否可以进一步整合?例如通过实施 ResultBuilder.For
?
您有一个 Result<'a, 'e> list
并且想要一个 Result<'a list, 'e>
。这听起来像https://fsharpforfunandprofit.com/posts/elevated-world-4/ (which has nothing to do with seq
s, despite what the name sounds like). A quick check of https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/result.fs中描述的sequence
函数表明该函数尚未在标准FSharp.Core库中实现,因此您需要自己实现它。
顺便说一句,如果您还没有读过 Scott Wlaschin 的 "Elevated World" 系列,我不建议您从我链接的文章中间开始。相反,从 this article 开始,因为它构建了您需要了解 "traverse" 和 "sequence" 函数作用的背景知识。然后你就会知道实现其中一个功能的一般模式。
关于你的第二个问题,能否再具体一些?例如,您希望 ResultBuilder.For
有什么行为? for
表达式的正常行为是采用 Result<'a, 'e> list
(或序列或数组)和 运行 内部块一次用于列表中的每个 Result<'a, 'e>
或序列或数组。如果您尝试在此处使用 all
函数,则 Result<'a, 'e>
(这是 F# 期望 CE 的 .For
方法产生的结果)和 [=11= 之间存在类型不匹配] 这就是您的 all
方法返回的内容。您具体希望 ResultBuilder.For
方法做什么?
这是我的解决方案,它采用结果序列(而不是列表),因此验证是惰性的。
let takeTo<'T> predicate (source: 'T seq) =
seq {
use en = source.GetEnumerator()
let mutable isDone = false
while isDone = false && en.MoveNext() do
yield en.Current
isDone <- predicate en.Current
}
let fromResults rs =
rs
|> Seq.scan (fun (vs, err) i ->
match i with
| Ok v -> (v::vs,err)
| Error e -> (vs, Some e)) ([], None)
|> Seq.takeTo (fun (vs, err) -> err.IsSome)
|> Seq.last
|> fun (vs, err) ->
match err with
| None -> vs |> List.rev |> Ok
| Some err -> Error err
此功能由 FsToolkit.ErrorHandling
包提供。函数 List.sequenceResultM
将 return 为:
- 一个
Result.Ok
包含所有 Ok
值的列表
- 或者,
Result.Error
仅包含第一个 Error
值
还有一个变体 List.sequenceResultA
,它 return 是找到的所有错误的列表。
#r "nuget: FsToolkit.ErrorHandling, 2.0.0"
open FsToolkit.ErrorHandling
let xs : Result<int, string> list =
[
Ok 123
Ok 456
]
let xa = List.sequenceResultA xs // Ok [123; 456]
let xm = List.sequenceResultM xs // Ok [123; 456]
printfn "xa: %A" xa
printfn "xm: %A" xm
let ys =
[
Ok 123
Ok 456
Error "abc"
Ok 789
]
let ya = List.sequenceResultA ys // Error ["abc"]
let ym = List.sequenceResultM ys // Error "abc"
printfn "ya: %A" ya
printfn "ym: %A" ym
let zs =
[
Ok 123
Error "abc"
Error "def"
Ok 456
]
let za = List.sequenceResultA zs // Error ["abc"; "def"]
let zm = List.sequenceResultM zs // Error "abc"
printfn "za: %A" za
printfn "zm: %A" zm
xa: Ok [123; 456]
xm: Ok [123; 456]
ya: Error ["abc"]
ym: Error "abc"
za: Error ["abc"; "def"]
zm: Error "abc"
在计算表达式方面(也由FsToolkit.ErrorHandling
提供),你可以这样做:
#r "nuget: FsToolkit.ErrorHandling, 2.0.0"
open FsToolkit.ErrorHandling
let printAll xs =
result {
let! xs = List.sequenceResultA xs
for x in xs do
printfn "%A" x
}
(注意这里的其他答案已经足够了!)
如果专门使用 List
s,可以使用 [=14= 的 Result
模块公开的 sequence
函数 (Result<'c, 'd> list -> Result<'c list, 'd>
) 来完成] 库(source 代码)。
但是,对于更通用的序列,可以使用 FsToolkit.ErrorHandling
库提供的 Seq.sequenceResultM
函数来完成。
我有一个 Result<'T, 'E> list
,我想按照这些规则将其变成一个 Result<'T list, 'E>
:
- 如果任何
Result
是Error
那么结果应该是Error
- 如果结果是
Error
,它应该是列表中的第一个Error
- 如果每个结果都是
OK
那么结果应该是Ok
并且列表顺序应该保持
所以我按如下方式实现了这一点:
let all xs =
let folder = fun state next ->
match (state, next) with
| (Result.Ok ys, Result.Ok y) -> ys |> List.append [ y ] |> Result.Ok
| (Result.Error e, _) -> Result.Error e
| (_, Result.Error e) -> Result.Error e
Seq.fold folder (Result.Ok []) xs
但是,这似乎已经在标准库中实现了。有吗?
其次,我有一个 Result
的计算表达式,如下所示:
type ResultBuilder () =
member this.Bind(x, f) =
match x with
| Result.Ok o -> f o
| Result.Error e -> Result.Error e
member this.Return(value) = Result.Ok value
member this.ReturnFrom(value) = value
let result = new ResultBuilder()
我可以在 result { ... }
中使用 all
但是否可以进一步整合?例如通过实施 ResultBuilder.For
?
您有一个 Result<'a, 'e> list
并且想要一个 Result<'a list, 'e>
。这听起来像https://fsharpforfunandprofit.com/posts/elevated-world-4/ (which has nothing to do with seq
s, despite what the name sounds like). A quick check of https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/result.fs中描述的sequence
函数表明该函数尚未在标准FSharp.Core库中实现,因此您需要自己实现它。
顺便说一句,如果您还没有读过 Scott Wlaschin 的 "Elevated World" 系列,我不建议您从我链接的文章中间开始。相反,从 this article 开始,因为它构建了您需要了解 "traverse" 和 "sequence" 函数作用的背景知识。然后你就会知道实现其中一个功能的一般模式。
关于你的第二个问题,能否再具体一些?例如,您希望 ResultBuilder.For
有什么行为? for
表达式的正常行为是采用 Result<'a, 'e> list
(或序列或数组)和 运行 内部块一次用于列表中的每个 Result<'a, 'e>
或序列或数组。如果您尝试在此处使用 all
函数,则 Result<'a, 'e>
(这是 F# 期望 CE 的 .For
方法产生的结果)和 [=11= 之间存在类型不匹配] 这就是您的 all
方法返回的内容。您具体希望 ResultBuilder.For
方法做什么?
这是我的解决方案,它采用结果序列(而不是列表),因此验证是惰性的。
let takeTo<'T> predicate (source: 'T seq) =
seq {
use en = source.GetEnumerator()
let mutable isDone = false
while isDone = false && en.MoveNext() do
yield en.Current
isDone <- predicate en.Current
}
let fromResults rs =
rs
|> Seq.scan (fun (vs, err) i ->
match i with
| Ok v -> (v::vs,err)
| Error e -> (vs, Some e)) ([], None)
|> Seq.takeTo (fun (vs, err) -> err.IsSome)
|> Seq.last
|> fun (vs, err) ->
match err with
| None -> vs |> List.rev |> Ok
| Some err -> Error err
此功能由 FsToolkit.ErrorHandling
包提供。函数 List.sequenceResultM
将 return 为:
- 一个
Result.Ok
包含所有Ok
值的列表 - 或者,
Result.Error
仅包含第一个Error
值
还有一个变体 List.sequenceResultA
,它 return 是找到的所有错误的列表。
#r "nuget: FsToolkit.ErrorHandling, 2.0.0"
open FsToolkit.ErrorHandling
let xs : Result<int, string> list =
[
Ok 123
Ok 456
]
let xa = List.sequenceResultA xs // Ok [123; 456]
let xm = List.sequenceResultM xs // Ok [123; 456]
printfn "xa: %A" xa
printfn "xm: %A" xm
let ys =
[
Ok 123
Ok 456
Error "abc"
Ok 789
]
let ya = List.sequenceResultA ys // Error ["abc"]
let ym = List.sequenceResultM ys // Error "abc"
printfn "ya: %A" ya
printfn "ym: %A" ym
let zs =
[
Ok 123
Error "abc"
Error "def"
Ok 456
]
let za = List.sequenceResultA zs // Error ["abc"; "def"]
let zm = List.sequenceResultM zs // Error "abc"
printfn "za: %A" za
printfn "zm: %A" zm
xa: Ok [123; 456]
xm: Ok [123; 456]
ya: Error ["abc"]
ym: Error "abc"
za: Error ["abc"; "def"]
zm: Error "abc"
在计算表达式方面(也由FsToolkit.ErrorHandling
提供),你可以这样做:
#r "nuget: FsToolkit.ErrorHandling, 2.0.0"
open FsToolkit.ErrorHandling
let printAll xs =
result {
let! xs = List.sequenceResultA xs
for x in xs do
printfn "%A" x
}
(注意这里的其他答案已经足够了!)
如果专门使用 List
s,可以使用 [=14= 的 Result
模块公开的 sequence
函数 (Result<'c, 'd> list -> Result<'c list, 'd>
) 来完成] 库(source 代码)。
但是,对于更通用的序列,可以使用 FsToolkit.ErrorHandling
库提供的 Seq.sequenceResultM
函数来完成。