Skip to content

Commit

Permalink
+ Result.Sequence & Option|VOption use Accumulator
Browse files Browse the repository at this point in the history
  • Loading branch information
fcallejon committed Nov 8, 2023
1 parent 1970fea commit 31ea507
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 26 deletions.
98 changes: 76 additions & 22 deletions src/FSharpPlus/Extensions/Extensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -126,40 +126,94 @@ module Extensions =
type Option<'t> with

/// Returns None if it contains a None element, otherwise a list of all elements
static member Sequence (t: seq<option<'T>>) =
let mutable ok = true
#if FABLE_COMPILER || FABLE_COMPILER_4 || FABLE_COMPILER_JAVASCRIPT
static member Sequence (t: seq<Result<_, _>>) =
let mutable error = None
let res = Seq.toArray (seq {
use e = t.GetEnumerator ()
while e.MoveNext () && ok do
while e.MoveNext () && error.IsNone do
match e.Current with
| Some v -> yield v
| None -> ok <- false })
if ok then Some (Array.toSeq res) else None

| Ok v -> yield v
| Error e -> error <- Some e })

match error with
| None -> Ok (Array.toSeq res)
| Some e -> Error e
#else
static member Sequence (t: seq<option<'T>>) =
let accumulator = ArrayCollector<'T> ()
let mutable noneFound = false
use e = t.GetEnumerator ()
while e.MoveNext () && noneFound do
match e.Current with
| Some v -> accumulator.Add v
| None -> noneFound <- true

if noneFound
then None
else
Some (accumulator.Close () |> Array.toSeq)
#endif

type ValueOption<'t> with

/// Returns None if it contains a None element, otherwise a list of all elements
#if FABLE_COMPILER || FABLE_COMPILER_4 || FABLE_COMPILER_JAVASCRIPT
static member Sequence (t: seq<voption<'T>>) =
let mutable ok = true
let res = Seq.toArray (seq {
use e = t.GetEnumerator ()
while e.MoveNext () && ok do
match e.Current with
| ValueSome v -> yield v
| ValueNone -> ok <- false })
if ok then ValueSome (Array.toSeq res) else ValueNone

let accumulator = ArrayCollector<'T> ()

Check failure on line 163 in src/FSharpPlus/Extensions/Extensions.fs

View workflow job for this annotation

GitHub Actions / testFable3SubsetOnCore

The value or constructor 'ArrayCollector' is not defined. Maybe you want one of the following:� Array� array� Array
let mutable noneFound = false
use e = t.GetEnumerator ()
while e.MoveNext () && noneFound do
match e.Current with
| ValueSome v -> accumulator.Add v

Check failure on line 168 in src/FSharpPlus/Extensions/Extensions.fs

View workflow job for this annotation

GitHub Actions / testFable3SubsetOnCore

Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
| ValueNone -> noneFound <- true

if noneFound
then ValueNone
else
ValueSome (accumulator.Close () |> Array.toSeq)

Check failure on line 174 in src/FSharpPlus/Extensions/Extensions.fs

View workflow job for this annotation

GitHub Actions / testFable3SubsetOnCore

Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
#else
static member Sequence (t: seq<voption<'T>>) =
let accumulator = ArrayCollector<'T> ()
let mutable noneFound = false
use e = t.GetEnumerator ()
while e.MoveNext () && noneFound do
match e.Current with
| ValueSome v -> accumulator.Add v
| ValueNone -> noneFound <- true

if noneFound
then ValueNone
else
ValueSome (accumulator.Close () |> Array.toSeq)
#endif

type Result<'t, 'error> with

/// Returns the first Error if it contains an Error element, otherwise a list of all elements
static member Sequence (t: seq<Result<'T, ' Error>>) =
let mutable bad = None
#if FABLE_COMPILER || FABLE_COMPILER_4 || FABLE_COMPILER_JAVASCRIPT
static member Sequence (t: seq<Result<_, _>>) =
let mutable error = None
let res = Seq.toArray (seq {
use e = t.GetEnumerator ()
while e.MoveNext () && bad.IsNone do
while e.MoveNext () && error.IsNone do
match e.Current with
| Ok v -> yield v
| Error x -> bad <- Some x })
match bad with
| None-> Ok (Array.toSeq res)
| Error e -> error <- Some e })

match error with
| None -> Ok (Array.toSeq res)
| Some e -> Error e
#else
static member Sequence (t: seq<Result<'T, ' Error>>) =
let accumulator = ArrayCollector<'T> ()
let mutable error = None
use e = t.GetEnumerator ()
while e.MoveNext () && error.IsNone do
match e.Current with
| Ok v -> accumulator.Add v
| Error x -> error <- Some x
match error with
| None-> Ok (accumulator.Close () |> Array.toSeq)
| Some x -> Error x
#endif
4 changes: 4 additions & 0 deletions tests/benchmarks/Benchmarks.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AsyncSequenceBenchmarks.fs" />
<Compile Include="ExtensionsBenchmarks.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\FSharpPlus\FSharpPlus.fsproj" />
</ItemGroup>
</Project>
170 changes: 170 additions & 0 deletions tests/benchmarks/ExtensionsBenchmarks.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
module ExtensionsBenchmarks

open FSharpPlus
open BenchmarkDotNet.Attributes
open Microsoft.FSharp.Core.CompilerServices

[<AutoOpen>]
module Common =
let mkArray s n invalidAtEnd arraySize =
if invalidAtEnd then
seq {
yield! Array.init arraySize (fun _ -> s(1))
yield n
}
|> Seq.toArray
else
seq {
yield! Array.init (arraySize / 2) (fun _ -> s(1))
yield n
yield! Array.init (arraySize / 2) (fun _ -> s(1))
}
|> Seq.toArray

module OptionOldVersion =
let sequence (t: seq<option<'T>>) =
let mutable ok = true
let res = Seq.toArray (seq {
use e = t.GetEnumerator ()
while e.MoveNext () && ok do
match e.Current with
| Some v -> yield v
| None -> ok <- false })
if ok then Some (Array.toSeq res) else None

module VOptionOldVersion =
let sequence (t: seq<voption<'T>>) =
let mutable ok = true
let res = Seq.toArray (seq {
use e = t.GetEnumerator ()
while e.MoveNext () && ok do
match e.Current with
| ValueSome v -> yield v
| ValueNone -> ok <- false })
if ok then ValueSome (Array.toSeq res) else ValueNone

module OptionNewVersion =

let sequence (t: seq<option<'T>>) =
let accumulator = ArrayCollector<'T> ()
let mutable noneFound = false
use e = t.GetEnumerator ()
while e.MoveNext () && noneFound do
match e.Current with
| Some v -> accumulator.Add v
| None -> noneFound <- true

if noneFound
then None
else
Some (accumulator.Close () |> Array.toSeq)

module VOptionNewVersion =

let sequence (t: seq<voption<'T>>) =
let accumulator = ArrayCollector<'T> ()
let mutable noneFound = false
use e = t.GetEnumerator ()
while e.MoveNext () && noneFound do
match e.Current with
| ValueSome v -> accumulator.Add v
| ValueNone -> noneFound <- true

if noneFound
then ValueNone
else
ValueSome (accumulator.Close () |> Array.toSeq)

module ResultOldVersion =
let sequence (t: seq<Result<_, _>>) =
let mutable error = None
let res = Seq.toArray (seq {
use e = t.GetEnumerator ()
while e.MoveNext () && error.IsNone do
match e.Current with
| Ok v -> yield v
| Error e -> error <- Some e })

match error with
| None -> Ok (Array.toSeq res)
| Some e -> Error e

module ResultNewVersion =

let sequence (t: seq<Result<int, string>>) : Result<int seq, string> =
let accumulator = ArrayCollector<'T> ()
let mutable error = None
use e = t.GetEnumerator ()
while e.MoveNext () && error.IsNone do
match e.Current with
| Ok v -> accumulator.Add v
| Error e -> error <- Some e

match error with
| None -> Ok (accumulator.Close () |> Array.toSeq)
| Some e -> Error e

type Values<'t> = { Title: string; Values: 't }

[<MemoryDiagnoser>]
type OptionBenchmarks() =

member this.runArray =
seq {
yield { Title = "1000_M"; Values = mkArray Some None false 1000 }
yield { Title = "10000_M"; Values = mkArray Some None false 10000 }
yield { Title = "100000_M"; Values = mkArray Some None false 100000 }
yield { Title = "1000_E"; Values = mkArray Some None true 1000 }
yield { Title = "10000_E"; Values = mkArray Some None true 10000 }
yield { Title = "100000_E"; Values = mkArray Some None true 100000 }
}

[<Benchmark(Baseline = true)>]
[<ArgumentsSource(nameof(Unchecked.defaultof<OptionBenchmarks>.runArray))>]
member this.Base (v: Values<int option array>) = OptionOldVersion.sequence v.Values

[<Benchmark>]
[<ArgumentsSource(nameof(Unchecked.defaultof<OptionBenchmarks>.runArray))>]
member this.NewVersion (v: Values<int option array>) = OptionNewVersion.sequence v.Values

[<MemoryDiagnoser>]
type VOptionBenchmarks() =

member this.runArray =
seq {
yield { Title = "1000_M"; Values = mkArray ValueSome ValueNone false 1000 }
yield { Title = "10000_M"; Values = mkArray ValueSome ValueNone false 10000 }
yield { Title = "100000_M"; Values = mkArray ValueSome ValueNone false 100000 }
yield { Title = "1000_E"; Values = mkArray ValueSome ValueNone true 1000 }
yield { Title = "10000_E"; Values = mkArray ValueSome ValueNone true 10000 }
yield { Title = "100000_E"; Values = mkArray ValueSome ValueNone true 100000 }
}

[<Benchmark(Baseline = true)>]
[<ArgumentsSource(nameof(Unchecked.defaultof<VOptionBenchmarks>.runArray))>]
member this.VOptionBase (v: Values<int voption array>) = VOptionOldVersion.sequence v.Values

[<Benchmark>]
[<ArgumentsSource(nameof(Unchecked.defaultof<VOptionBenchmarks>.runArray))>]
member this.NewVersion (v: Values<int voption array>) = VOptionNewVersion.sequence v.Values

[<MemoryDiagnoser>]
type ResultBenchmarks() =

member this.runArray =
seq {
yield { Title = "1000_M"; Values = mkArray Ok (Error "error") false 1000 }
yield { Title = "10000_M"; Values = mkArray Ok (Error "error") false 10000 }
yield { Title = "100000_M"; Values = mkArray Ok (Error "error") false 100000 }
yield { Title = "1000_E"; Values = mkArray Ok (Error "error") true 1000 }
yield { Title = "10000_E"; Values = mkArray Ok (Error "error") true 10000 }
yield { Title = "100000_E"; Values = mkArray Ok (Error "error") true 100000 }
}

[<Benchmark(Baseline = true)>]
[<ArgumentsSource(nameof(Unchecked.defaultof<ResultBenchmarks>.runArray))>]
member this.ResultBase (v: Values<Result<int, string> array>) = ResultOldVersion.sequence v.Values

[<Benchmark>]
[<ArgumentsSource(nameof(Unchecked.defaultof<ResultBenchmarks>.runArray))>]
member this.NewVersion (v: Values<Result<int, string> array>) = ResultNewVersion.sequence v.Values
8 changes: 4 additions & 4 deletions tests/benchmarks/Program.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
open AsyncSequences

[<EntryPoint>]
let main _ =
do BenchmarkDotNet.Running.BenchmarkRunner.Run<Benchmarks>() |> ignore
0
// do BenchmarkDotNet.Running.BenchmarkRunner.Run<ExtensionsBenchmarks.OptionBenchmarks>() |> ignore
// do BenchmarkDotNet.Running.BenchmarkRunner.Run<ExtensionsBenchmarks.VOptionBenchmarks>() |> ignore
do BenchmarkDotNet.Running.BenchmarkRunner.Run<ExtensionsBenchmarks.ResultBenchmarks>() |> ignore
0

0 comments on commit 31ea507

Please sign in to comment.