From f23dd0ebb0f4d972665a2b7e3eacbdbf6ef58937 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Wed, 14 Feb 2024 23:55:06 +0000 Subject: [PATCH] refactor(Seq.sequenceResultM)!: Change Ok to Array --- paket.dependencies | 13 ++--- src/FsToolkit.ErrorHandling/Seq.fs | 34 +++++++------ tests/FsToolkit.ErrorHandling.Tests/Seq.fs | 58 +++++++++------------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/paket.dependencies b/paket.dependencies index 162993e7..f73a2736 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -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 diff --git a/src/FsToolkit.ErrorHandling/Seq.fs b/src/FsToolkit.ErrorHandling/Seq.fs index 8a3fe81f..281a2ba1 100644 --- a/src/FsToolkit.ErrorHandling/Seq.fs +++ b/src/FsToolkit.ErrorHandling/Seq.fs @@ -1,19 +1,21 @@ -namespace FsToolkit.ErrorHandling - [] -module Seq = +module FsToolkit.ErrorHandling.Seq + +let sequenceResultM (xs: seq>) : 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 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 diff --git a/tests/FsToolkit.ErrorHandling.Tests/Seq.fs b/tests/FsToolkit.ErrorHandling.Tests/Seq.fs index 522b7041..925d92f0 100644 --- a/tests/FsToolkit.ErrorHandling.Tests/Seq.fs +++ b/tests/FsToolkit.ErrorHandling.Tests/Seq.fs @@ -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" ]