Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(Seq.sequenceResultM)!: Change Ok to Array
Browse files Browse the repository at this point in the history
bartelink committed Feb 14, 2024
1 parent dccdbbc commit d9d98cf
Showing 3 changed files with 63 additions and 62 deletions.
13 changes: 4 additions & 9 deletions paket.dependencies
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
source https://api.nuget.org/v3/index.json


storage: none

nuget Microsoft.SourceLink.GitHub prerelease copy_local: true




group NetStandard2
source https://api.nuget.org/v3/index.json
lowest_matching: true
strategy: min
nuget FSharp.Core >= 4.7.2
nuget FSharp.Core >= 4.7.2 content: none
nuget Ply
nuget Hopac
nuget FSharp.Control.AsyncSeq
@@ -21,9 +17,6 @@ framework: netstandard2.0, net6.0
storage: none
condition: netstandard2_0




group NetStandard2_1
source https://api.nuget.org/v3/index.json
lowest_matching: true
@@ -36,6 +29,7 @@ framework: netstandard2.1, net7.0
storage: none
condition: netstandard2_1


group Test
source https://api.nuget.org/v3/index.json
storage: none
@@ -52,6 +46,7 @@ nuget Fable.Python
nuget Fable.Pyxpecto
nuget Ply


group Benchmarks
source https://api.nuget.org/v3/index.json
storage: none
@@ -65,7 +60,7 @@ nuget BenchmarkDotNet.Diagnostics.Windows 0.13.1
group Build
source https://api.nuget.org/v3/index.json
storage: none
nuget FSharp.Core
nuget FSharp.Core content: none
nuget Fake.Core.Target 5.22.0
nuget Fake.DotNet.Cli 5.22.0
nuget Fake.Core.ReleaseNotes 5.22.0
54 changes: 36 additions & 18 deletions src/FsToolkit.ErrorHandling/Seq.fs
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
namespace FsToolkit.ErrorHandling

[<RequireQualifiedAccess>]
module Seq =

let sequenceResultM (xs: seq<Result<'t, 'e>>) : Result<'t seq, 'e> =
let rec loop xs ts =
match Seq.tryHead xs with
| Some x ->
x
|> Result.bind (fun t -> loop (Seq.tail xs) (t :: ts))
| None ->
Ok(
List.rev ts
|> List.toSeq
)

// Seq.cache prevents double evaluation in Seq.tail
loop (Seq.cache xs) []
module FsToolkit.ErrorHandling.Seq

let sequenceResultM (xs: seq<Result<'t, 'e>>) : Result<'t[], 'e> =
if isNull xs then
nullArg (nameof xs)

let acc = ResizeArray()
let mutable err = Unchecked.defaultof<_>
let mutable ok = true
use e = xs.GetEnumerator()

while e.MoveNext()
&& ok do
match e.Current with
| Ok r -> acc.Add r
| Error e ->
ok <- false
err <- e

if ok then Ok(acc.ToArray()) else Error err

let sequenceResultA (xs: seq<Result<'t, 'e>>) : Result<'t[], 'e[]> =
if isNull xs then
nullArg (nameof xs)

let oks = ResizeArray()
let errs = ResizeArray()

for x in xs do
match x with
| Ok r -> oks.Add r
| Error e -> errs.Add e

match errs.ToArray() with
| [||] -> Ok(oks.ToArray())
| errs -> Error errs
58 changes: 23 additions & 35 deletions tests/FsToolkit.ErrorHandling.Tests/Seq.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module SeqTests


#if FABLE_COMPILER_PYTHON
open Fable.Pyxpecto
#endif
@@ -12,60 +11,50 @@ open Expecto
#endif
open SampleDomain
open TestData
open TestHelpers
open System
open FsToolkit.ErrorHandling


let sequenceResultMTests =
testList "Seq.sequenceResultM Tests" [
testCase "traverseResult with an empty sequence"
testCase "sequenceResult with an empty sequence"
<| fun _ ->
let tweets = []
let expected = Ok []
let tweets = Seq.empty
let expected = Ok [||]

let actual =
Seq.sequenceResultM (Seq.map Tweet.TryCreate tweets)
|> Result.map Seq.toList
let actual = Seq.sequenceResultM (Seq.map Tweet.TryCreate tweets)

Expect.equal actual expected "Should have an empty list of valid tweets"

testCase "traverseResult with a sequence of valid data"
testCase "sequenceResult with a sequence of valid data"
<| fun _ ->
let tweets = [
"Hi"
"Hello"
"Hola"
]
let tweets =
seq {
"Hi"
"Hello"
"Hola"
}

let expected =
List.map tweet tweets
|> Ok
let expected = Ok [| for x in tweets -> tweet x |]

let actual =
Seq.sequenceResultM (Seq.map Tweet.TryCreate tweets)
|> Result.map Seq.toList
let actual = Seq.sequenceResultM (Seq.map Tweet.TryCreate tweets)

Expect.equal actual expected "Should have a list of valid tweets"

testCase "sequenceResultM with few invalid data"
testCase "sequenceResult with few invalid data"
<| fun _ ->
let tweets =
[
seq {
""
"Hello"
aLongerInvalidTweet
]
:> seq<_>
}

let expected = Error emptyTweetErrMsg

let actual = Seq.sequenceResultM (Seq.map Tweet.TryCreate tweets)

Expect.equal
actual
(Error emptyTweetErrMsg)
"traverse the sequence and return the first error"
Expect.equal actual expected "traverse the sequence and return the first error"

testCase "sequenceResultM stops after first invalid data"
testCase "sequenceResult stops after first invalid data"
<| fun _ ->
let mutable counter = 0

@@ -81,12 +70,11 @@ let sequenceResultMTests =
+ 1
}

let expected = Error longerTweetErrMsg

let actual = Seq.sequenceResultM (Seq.map Tweet.TryCreate tweets)

Expect.equal
actual
(Error longerTweetErrMsg)
"traverse the sequence and return the first error"
Expect.equal actual expected "traverse the sequence and return the first error"

Expect.equal counter 0 "evaluation of the sequence stops at the first error"
]

0 comments on commit d9d98cf

Please sign in to comment.