Skip to content

Commit

Permalink
refactor(Seq.sequenceResultM)!: Change Ok to Array
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Feb 15, 2024
1 parent dccdbbc commit f23dd0e
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 60 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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -52,6 +46,7 @@ nuget Fable.Python
nuget Fable.Pyxpecto
nuget Ply


group Benchmarks
source https://api.nuget.org/v3/index.json
storage: none
Expand All @@ -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
Expand Down
34 changes: 18 additions & 16 deletions src/FsToolkit.ErrorHandling/Seq.fs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
namespace FsToolkit.ErrorHandling

[<RequireQualifiedAccess>]
module Seq =
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()

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

// Seq.cache prevents double evaluation in Seq.tail
loop (Seq.cache xs) []
if ok then Ok(acc.ToArray()) else Error err
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
Expand All @@ -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

Expand All @@ -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"
]
Expand Down

0 comments on commit f23dd0e

Please sign in to comment.