diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ae4f28e5..62cdc913 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,6 @@ +### 4.16.0 +- [refactor!: Seq.sequenceResultM returns Array instead of seq](https://github.com/demystifyfp/FsToolkit.ErrorHandling/pull/255) [@bartelink](https://github.com/bartelink) + ### 4.15.1 - January 15, 2024 - [Doc updates](https://github.com/demystifyfp/FsToolkit.ErrorHandling/pull/247) Credits @1eyewonder diff --git a/gitbook/SUMMARY.md b/gitbook/SUMMARY.md index b28bc49c..af5244eb 100644 --- a/gitbook/SUMMARY.md +++ b/gitbook/SUMMARY.md @@ -25,6 +25,8 @@ * [sequenceResultM](list/sequenceResultM.md) * [traverseResultA](list/traverseResultA.md) * [sequenceResultA](list/sequenceResultA.md) + * Seqs + * [sequenceResultM](seq/sequenceResultM.md) * Transforms * [ofChoice](result/ofChoice.md) diff --git a/gitbook/seq/sequenceResultM.md b/gitbook/seq/sequenceResultM.md new file mode 100644 index 00000000..e2f821e1 --- /dev/null +++ b/gitbook/seq/sequenceResultM.md @@ -0,0 +1,69 @@ +# Seq.sequenceResultM + +Namespace: `FsToolkit.ErrorHandling` + +## Function Signature + +```fsharp +seq> -> Result<'a[], 'b> +``` + +This is monadic, stopping on the first error. Compare the example below with [sequenceResultA](sequenceResultA.md). + +See also Scott Wlaschin's [Understanding traverse and sequence](https://fsharpforfunandprofit.com/posts/elevated-world-4/). + +## Examples + +### Example 1 + +```fsharp +// string -> Result +let tryParseInt str = + match Int32.TryParse str with + | true, x -> Ok x + | false, _ -> Error $"unable to parse '{str}' to integer" + +["1"; "2"; "3"] +|> Seq.map tryParseInt +|> Seq.sequenceResultM +// Ok [| 1; 2; 3 |] + +seq { "1"; "foo"; "3"; "bar" } +|> Seq.map tryParseInt +|> Seq.sequenceResultM +// Error "unable to parse 'foo' to integer" +``` + +### Example 2 + +```fsharp +// int -> Result +let isPrime (x: int) = + if x < 2 then Error $"{x} must be greater than 1" + elif x = 2 then Ok true + else + let rec isPrime' (x : int) (i : int) = + if i * i > x then Ok true + elif x % i = 0 then Ok false + else isPrime' x (i + 1) + isPrime' x 2 + +// int seq -> Result +let checkIfAllPrime (numbers: seq) = + numbers + |> Seq.map isPrime // Result list + |> Seq.sequenceResultM // Result + |> Result.map (Array.forall id) // shortened version of '|> Result.map (fun bools -> bools |> Array.forall (fun x -> x = true))' + +let a = [ 1; 2; 3; 4; 5 ] |> checkIfAllPrime +// Error [| "1 must be greater than 1" |] + +let b = [| 1; 2; 3; 4; 5; 0 |] |> checkIfAllPrime +// Error [| "1 must be greater than 1" |] + +let a = seq { 2; 3; 4; 5 } |> checkIfAllPrime +// Ok false + +let a = [2; 3; 5;] |> checkIfAllPrime +// Ok true +``` diff --git a/tests/FsToolkit.ErrorHandling.Tests/Seq.fs b/tests/FsToolkit.ErrorHandling.Tests/Seq.fs index 925d92f0..908d138d 100644 --- a/tests/FsToolkit.ErrorHandling.Tests/Seq.fs +++ b/tests/FsToolkit.ErrorHandling.Tests/Seq.fs @@ -9,13 +9,13 @@ open Fable.Mocha #if !FABLE_COMPILER open Expecto #endif +open FsToolkit.ErrorHandling open SampleDomain open TestData -open FsToolkit.ErrorHandling let sequenceResultMTests = testList "Seq.sequenceResultM Tests" [ - testCase "sequenceResult with an empty sequence" + testCase "empty sequence" <| fun _ -> let tweets = Seq.empty let expected = Ok [||] @@ -24,7 +24,7 @@ let sequenceResultMTests = Expect.equal actual expected "Should have an empty list of valid tweets" - testCase "sequenceResult with a sequence of valid data" + testCase "valid data" <| fun _ -> let tweets = seq { @@ -39,7 +39,7 @@ let sequenceResultMTests = Expect.equal actual expected "Should have a list of valid tweets" - testCase "sequenceResult with few invalid data" + testCase "valid and invalid data" <| fun _ -> let tweets = seq { @@ -54,7 +54,7 @@ let sequenceResultMTests = Expect.equal actual expected "traverse the sequence and return the first error" - testCase "sequenceResult stops after first invalid data" + testCase "stops after first invalid data" <| fun _ -> let mutable counter = 0