diff --git a/src/FSharpPlus/Control/MonadOps.fs b/src/FSharpPlus/Control/MonadOps.fs index 9e7409f20..ae972a90d 100644 --- a/src/FSharpPlus/Control/MonadOps.fs +++ b/src/FSharpPlus/Control/MonadOps.fs @@ -8,6 +8,8 @@ module internal MonadOps = let inline (>>=) x f = Bind.Invoke x f let inline result x = Return.Invoke x let inline (<*>) f x = Apply.Invoke f x + let inline presult x = ParReturn.Invoke x + let inline () f x = ParApply.Invoke f x let inline (<|>) x y = Append.Invoke x y let inline (>=>) (f: 'a->'``Monad<'b>``) (g: 'b->'``Monad<'c>``) (x: 'a) : '``Monad<'c>`` = f x >>= g diff --git a/src/FSharpPlus/Control/Parallel.fs b/src/FSharpPlus/Control/Parallel.fs index d966b007b..66d90911b 100644 --- a/src/FSharpPlus/Control/Parallel.fs +++ b/src/FSharpPlus/Control/Parallel.fs @@ -1,6 +1,7 @@ namespace FSharpPlus.Control open System +open System.Text open System.Runtime.InteropServices open System.Collections.Generic open System.Threading.Tasks @@ -53,6 +54,15 @@ type ParReturn = [", 10720, IsError = true)>] static member ParReturn (x: ResizeArray<'a>, _: ParReturn ) = Return.Return (x, Unchecked.defaultof) + //Restricted + [] + static member ParReturn (_: string , _: ParReturn ) = fun (x: char) -> string x : string + [] + static member ParReturn (_: StringBuilder , _: ParReturn ) = fun (x: char) -> new StringBuilder (string x) : StringBuilder + [] + static member ParReturn (_: 'a Set , _: ParReturn ) = fun (x: 'a ) -> Set.singleton x + static member ParReturn (_: 'a Set2 , _: ParReturn ) = fun (_: 'a ) -> Set2() : 'a Set2 + #endif type ParApply = @@ -80,7 +90,12 @@ type ParApply = static member ```` (struct (f: voption<_> , x: voption<'T> ), []_output: voption<'U> , []_mthd: ParApply) = Apply.``<*>`` (struct (f, x), _output, Unchecked.defaultof) static member inline ```` (struct (f: Result<_,'E> , x: Result<'T,'E> ), []_output: Result<'b,'E> , []_mthd: ParApply) : Result<'U, 'E> = Result.apply2With Plus.Invoke (<|) f x static member inline ```` (struct (f: Choice<_,'E> , x: Choice<'T,'E> ), []_output: Choice<'b,'E> , []_mthd: ParApply) : Choice<'U, 'E> = Choice.apply2With Plus.Invoke (<|) f x - static member inline ```` (struct (f: KeyValuePair<'Key,_>, x: KeyValuePair<'Key,'T>), []_output: KeyValuePair<'Key,'U>, []_mthd: ParApply) = Apply.``<*>`` (struct (f, x), _output, Unchecked.defaultof) + static member inline ```` (struct (f: KeyValuePair<'Key,_>, x: KeyValuePair<'Key,'T>), []_output: KeyValuePair<'Key,'U>, []_mthd: Default2) = Apply.``<*>`` (struct (f, x), _output, Unchecked.defaultof) + static member inline ```` (struct (f: KeyValuePair2<_,_>, x: KeyValuePair2<_,'T> ) , _output: KeyValuePair2<_,'U> , _mthd: Default2) : KeyValuePair2<'Key,'U> = + let a, b = f.Key, x.Key + let f, x = f.Value, x.Value + KeyValuePair2 (Plus.Invoke a b, f x) + static member ```` (struct (f: Map<'Key,_> , x: Map<'Key,'T> ), []_output: Map<'Key,'U> , []_mthd: ParApply) : Map<'Key,'U> = Apply.``<*>`` (struct (f, x), _output, Unchecked.defaultof) static member ```` (struct (f: Dictionary<'Key,_> , x: Dictionary<'Key,'T> ), []_output: Dictionary<'Key,'U> , []_mthd: ParApply) : Dictionary<'Key,'U> = Apply.``<*>`` (struct (f, x), _output, Unchecked.defaultof) diff --git a/src/FSharpPlus/Control/Traversable.fs b/src/FSharpPlus/Control/Traversable.fs index 9b4363c3d..08122741e 100644 --- a/src/FSharpPlus/Control/Traversable.fs +++ b/src/FSharpPlus/Control/Traversable.fs @@ -244,4 +244,247 @@ type Sequence with let inline call (a: 'a, b: 'b) = call_3 (a, b, Unchecked.defaultof<'R>) : 'R call (Unchecked.defaultof, t) -#endif + + +// Parallel traversables + + +type ParSequence = + inherit Default1 + static member inline InvokeOnInstance (t: '``ParTraversable<'Functor<'T>>``) = (^``ParTraversable<'Functor<'T>>`` : (static member ParSequence : _ -> _) t) : '``Functor<'ParTraversable<'T>>`` + + [] + static member inline ForInfiniteSequences (t: seq<_>, isFailure, conversion) = + let add x y = y :: x + let mutable go = true + let mutable r = Unchecked.defaultof<_> + let mutable isEmpty = true + use e = t.GetEnumerator () + while go && e.MoveNext () do + if isFailure e.Current then go <- false + if isEmpty then r <- Map.Invoke List.singleton e.Current + else r <- Map.Invoke add r e.Current + isEmpty <- false + if isEmpty then ParReturn.Invoke (conversion []) + else Map.Invoke (List.rev >> conversion) r + +type ParTraverse = + inherit Default1 + static member inline InvokeOnInstance f (t: ^a) = (^a : (static member ParTraverse : _ * _ -> 'R) t, f) + + static member inline ParTraverse (t: '``ParTraversable<'T>``, f: 'T -> '``Functor<'U>``, []_output: '``Functor<'ParTraversable<'U>>``, []_impl: Default4) = + #if TEST_TRACE + Traces.add "ParTraverse 'ParTraversable, 'T -> Functor<'U>" + #endif + let mapped = Map.Invoke f t : '``ParTraversable<'Functor<'U>>`` + (^``ParTraversable<'T>`` : (static member ParSequence : _ -> _) mapped) : '``Functor<'ParTraversable<'U>>`` + + static member inline ParTraverse (t: Id<_>, f, []_output: 'R, []_impl: Default3) = + #if TEST_TRACE + Traces.add "ParTraverse Id" + #endif + Map.Invoke Id.create (f (Id.run t)) + + static member inline ParTraverse (t: _ seq, f, []_output: 'R, []_impl: Default3) = + #if TEST_TRACE + Traces.add "ParTraverse seq" + #endif + let cons x y = seq {yield x; yield! y} + let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) (f x) ys + Seq.foldBack cons_f t (ParReturn.Invoke Seq.empty) + + static member inline ParTraverse (t: _ NonEmptySeq, f, []_output: 'R, []_impl: Default3) = + #if TEST_TRACE + Traces.add "ParTraverse NonEmptySeq" + #endif + let cons x y = seq {yield x; yield! y} + let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) (f x) ys + Map.Invoke NonEmptySeq.ofSeq (Seq.foldBack cons_f t (ParReturn.Invoke Seq.empty)) + + static member inline ParTraverse (t: seq<'T>, f: 'T -> '``Functor<'U>``, []_output: '``Functor>``, []_impl: Default2) = + #if TEST_TRACE + Traces.add "ParTraverse seq, 'T -> Functor<'U>" + #endif + let mapped = Seq.map f t + ParSequence.ForInfiniteSequences (mapped, IsParLeftZero.Invoke, List.toSeq) : '``Functor>`` + + static member inline ParTraverse (t: NonEmptySeq<'T>, f: 'T -> '``Functor<'U>``, []_output: '``Functor>``, []_impl: Default2) = + #if TEST_TRACE + Traces.add "ParTraverse NonEmptySeq, 'T -> Functor<'U>" + #endif + let mapped = NonEmptySeq.map f t + ParSequence.ForInfiniteSequences (mapped, IsParLeftZero.Invoke, NonEmptySeq.ofList) : '``Functor>`` + + static member inline ParTraverse (t: ^a, f, []_output: 'R, []_impl: Default1) : 'R = + #if TEST_TRACE + Traces.add "ParTraverse ^a" + #endif + ParTraverse.InvokeOnInstance f t + static member inline ParTraverse (_: ^a when ^a : null and ^a :struct, _, _: 'R, _impl: Default1) = id + + #if !FABLE_COMPILER + static member ParTraverse (t: 't seq, f: 't -> Async<'u>, []_output: Async>, []_impl: ParTraverse) : Async> = async { + #if TEST_TRACE + Traces.add "ParTraverse 't seq, 't -> Async<'u>" + #endif + + let! ct = Async.CancellationToken + return seq { + use enum = t.GetEnumerator () + while enum.MoveNext() do + yield Async.RunSynchronously (f enum.Current, cancellationToken = ct) }} + #endif + + #if !FABLE_COMPILER + static member ParTraverse (t: 't NonEmptySeq, f: 't -> Async<'u>, []_output: Async>, []_impl: ParTraverse) : Async> = async { + #if TEST_TRACE + Traces.add "ParTraverse 't NonEmptySeq, 't -> Async<'u>" + #endif + + let! ct = Async.CancellationToken + return seq { + use enum = t.GetEnumerator () + while enum.MoveNext() do + yield Async.RunSynchronously (f enum.Current, cancellationToken = ct) } |> NonEmptySeq.unsafeOfSeq } + #endif + + static member ParTraverse (t: Id<'t>, f: 't -> option<'u>, []_output: option>, []_impl: ParTraverse) = + #if TEST_TRACE + Traces.add "ParTraverse Id, 't -> option<'u>" + #endif + Option.map Id.create (f (Id.run t)) + + static member inline ParTraverse (t: option<_>, f, []_output: 'R, []_impl: ParTraverse) : 'R = + #if TEST_TRACE + Traces.add "ParTraverse option" + #endif + match t with Some x -> Map.Invoke Some (f x) | _ -> ParReturn.Invoke None + + static member inline ParTraverse (t: voption<_>, f, []_output: 'R, []_impl: ParTraverse) : 'R = + #if TEST_TRACE + Traces.add "ParTraverse voption" + #endif + match t with ValueSome x -> Map.Invoke ValueSome (f x) | _ -> ParReturn.Invoke ValueNone + + static member inline ParTraverse (t:Map<_,_> , f, []_output: 'R, []_impl: ParTraverse) : 'R = + #if TEST_TRACE + Traces.add "ParTraverse Map" + #endif + let insert_f m k v = Map.Invoke (Map.add k) v m + Map.fold insert_f (ParReturn.Invoke Map.empty) (Map.mapValues f t) + + static member inline ParTraverse (t: Result<'T,'Error>, f: 'T -> '``Functor<'U>``, []_output: '``Functor>``, []_impl: ParTraverse) : '``Functor>`` = + #if TEST_TRACE + Traces.add "ParTraverse Result, 'T -> Functor<'U>" + #endif + match t with + | Ok a -> Map.Invoke Result<'U, 'Error>.Ok (f a) + | Error e -> ParReturn.Invoke (Result<'U, 'Error>.Error e) + + static member inline ParTraverse (t: Choice<'T,'Error>, f: 'T -> '``Functor<'U>``, []_output: '``Functor>``, []_impl: ParTraverse) : '``Functor>`` = + #if TEST_TRACE + Traces.add "ParTraverse Choice, 'T -> Functor<'U>" + #endif + match t with + | Choice1Of2 a -> Map.Invoke Choice<'U,'Error>.Choice1Of2 (f a) + | Choice2Of2 e -> ParReturn.Invoke (Choice<'U,'Error>.Choice2Of2 e) + + static member inline ParTraverse (t:list<_>,f , []_output: 'R, []_impl: ParTraverse) : 'R = + #if TEST_TRACE + Traces.add "ParTraverse list" + #endif + let rec loop acc = function + | [] -> acc + | x::xs -> + let v = f x + loop (v::acc) xs + let cons_f x xs = Map.Invoke List.cons xs x + List.fold cons_f (ParReturn.Invoke []) (loop [] t) + + static member inline ParTraverse (t:_ [],f , []_output: 'R, []_impl: ParTraverse) : 'R = + #if TEST_TRACE + Traces.add "ParTraverse []" + #endif + let cons x y = Array.append [|x|] y + let rec loop acc = function + | [||] -> acc + | xxs -> + let x, xs = Array.head xxs, Array.tail xxs + let v = f x + loop (cons v acc) xs + let cons_f x xs = Map.Invoke cons xs x + Array.fold cons_f (ParReturn.Invoke [||]) (loop [||] t) + + static member inline Invoke (f: 'T -> '``Functor<'U>``) (t: '``ParTraversable<'T>``) : '``Functor<'ParTraversable<'U>>`` = + let inline call_3 (a: ^a, b: ^b, c: ^c, f) = ((^a or ^b or ^c) : (static member ParTraverse : _*_*_*_ -> _) b, f, c, a) + let inline call (a: 'a, b: 'b, f) = call_3 (a, b, Unchecked.defaultof<'R>, f) : 'R + call (Unchecked.defaultof, t, f) + + +type ParSequence with + + static member inline ParSequence (t: _ seq, []_output: 'R, []_impl: Default5) : 'R = + let cons x y = seq { yield x; yield! y } + let cons_f x ys = Map.Invoke (cons: 'a -> seq<_> -> seq<_>) x ys + Seq.foldBack cons_f t (ParReturn.Invoke Seq.empty) + + static member inline ParSequence (t: seq<'``Applicative<'T>``>, []_output: '``Applicative>`` , []_impl: Default4) : '``Applicative>`` = + ParSequence.ForInfiniteSequences (t, IsParLeftZero.Invoke, List.toSeq) + + static member ParSequence (t: seq> , []_output: option> , []_impl: Default3) : option> = Option.Sequence t + #if !FABLE_COMPILER + static member ParSequence (t: seq> , []_output: voption> , []_impl: Default3) : voption> = ValueOption.Sequence t + #endif + static member inline ParSequence (t: seq>, []_output: Result, 'e>, []_impl: Default3) : Result, 'e> = Result.Parallel ((++), t) + static member inline ParSequence (t: seq>, []_output: Choice, 'e>, []_impl: Default3) : Choice, 'e> = Choice.Parallel ((++), t) + static member ParSequence (t: seq> , []_output: list> , []_impl: Default3) : list> = ParSequence.ForInfiniteSequences (t, List.isEmpty, List.toSeq) + // static member ParSequence (t: seq<'t []> , []_output: seq<'t> [] , []_impl: Default3) : seq<'t> [] = ParSequence.ForInfiniteSequences (t, Array.isEmpty, List.toSeq) + + #if !FABLE_COMPILER + static member ParSequence (t: seq> , []_output: Async> , []_impl: Default3) : Async> = Async.Parallel t |> Async.map Array.toSeq + #endif + static member inline ParSequence (t: NonEmptySeq<'``Applicative<'T>``>, []_output: '``Applicative>`` , []_impl: Default4) : '``Applicative>`` = ParSequence.ForInfiniteSequences (t, IsParLeftZero.Invoke, NonEmptySeq.ofList) + static member ParSequence (t: NonEmptySeq> , []_output: option> , []_impl: Default3) : option> = Option.Sequence t |> Option.map NonEmptySeq.unsafeOfSeq + static member inline ParSequence (t: NonEmptySeq>, []_output: Result, 'e>, []_impl: Default3) : Result, 'e> = Result.Parallel ((++), t) |> Result.map NonEmptySeq.unsafeOfSeq + static member inline ParSequence (t: NonEmptySeq>, []_output: Choice, 'e>, []_impl: Default3) : Choice, 'e> = Choice.Parallel ((++), t) |> Choice.map NonEmptySeq.unsafeOfSeq + static member ParSequence (t: NonEmptySeq> , []_output: list> , []_impl: Default3) : list> = ParSequence.ForInfiniteSequences(t, List.isEmpty , NonEmptySeq.ofList) + // static member ParSequence (t: NonEmptySeq<'t []> , []_output: NonEmptySeq<'t> [] , []_impl: Default3) : NonEmptySeq<'t> [] = ParSequence.ForInfiniteSequences(t, Array.isEmpty, NonEmptySeq.ofList) + #if !FABLE_COMPILER + static member ParSequence (t: NonEmptySeq> , []_output: Async> , []_impl: Default3) = Async.Parallel t |> Async.map NonEmptySeq.unsafeOfSeq : Async> + #endif + + static member inline ParSequence (t: ^a , []_output: 'R, []_impl: Default2) : 'R = ParTraverse.InvokeOnInstance id t + static member inline ParSequence (t: ^a , []_output: 'R, []_impl: Default1) : 'R = ParSequence.InvokeOnInstance t + + static member inline ParSequence (t: option<_> , []_output: 'R, []_impl: ParSequence) : 'R = match t with Some x -> Map.Invoke Some x | _ -> ParReturn.Invoke None + #if !FABLE_COMPILER + static member inline ParSequence (t: voption<_>, []_output: 'R, []_impl: ParSequence) : 'R = match t with ValueSome x -> Map.Invoke ValueSome x | _ -> ParReturn.Invoke ValueNone + #endif + static member inline ParSequence (t: list<_> , []_output: 'R, []_impl: ParSequence) : 'R = ParSequence.ForInfiniteSequences(t, IsParLeftZero.Invoke, id) + + static member inline ParSequence (t: Map<_,_> , []_output: 'R, []_impl: ParSequence) : 'R = + let insert_f k x ys = Map.Invoke (Map.add k) x ys + Map.foldBack insert_f t (ParReturn.Invoke Map.empty) + + static member inline ParSequence (t: Result<'``Functor<'T>``,'Error>, []_output: '``Functor>``, []_impl: ParSequence) : '``Functor>`` = + match t with + | Ok a -> Map.Invoke Result<'T,'Error>.Ok a + | Error e -> ParReturn.Invoke (Result<'T,'Error>.Error e) + + static member inline ParSequence (t: Choice<'``Functor<'T>``,'Error>, []_output: '``Functor>``, []_impl: ParSequence) : '``Functor>`` = + match t with + | Choice1Of2 a -> Map.Invoke Choice<'T,'Error>.Choice1Of2 a + | Choice2Of2 e -> ParReturn.Invoke (Choice<'T,'Error>.Choice2Of2 e) + + static member inline ParSequence (t: _ [] , []_output: 'R , []_impl: ParSequence) : 'R = ParSequence.ForInfiniteSequences(t, IsParLeftZero.Invoke, Array.ofList) + + static member inline ParSequence (t: Id<'``Functor<'T>``> , []_output: '``Functor>`` , []_impl: ParSequence) : '``Functor>`` = ParTraverse.Invoke id t + + static member inline ParSequence (t: ResizeArray<'``Functor<'T>``>, []_output: '``Functor>`` , []_impl: ParSequence) : '``Functor>``= ParTraverse.Invoke id t + + static member inline Invoke (t: '``ParTraversable<'Applicative<'T>>``) : '``Applicative<'ParTraversable<'T>>`` = + let inline call_3 (a: ^a, b: ^b, c: ^c) = ((^a or ^b or ^c) : (static member ParSequence : _*_*_ -> _) b, c, a) + let inline call (a: 'a, b: 'b) = call_3 (a, b, Unchecked.defaultof<'R>) : 'R + call (Unchecked.defaultof, t) + +#endif \ No newline at end of file diff --git a/src/FSharpPlus/Extensions/Extensions.fs b/src/FSharpPlus/Extensions/Extensions.fs index 1d22dd65b..d34a8fc97 100644 --- a/src/FSharpPlus/Extensions/Extensions.fs +++ b/src/FSharpPlus/Extensions/Extensions.fs @@ -208,6 +208,23 @@ module Extensions = | ValueSome x -> Choice2Of2 x #endif + /// Returns all Errors combined, otherwise a sequence of all elements. + static member Parallel (combiner, t: seq>) = + let mutable error = ValueNone + let res = Seq.toArray (seq { + use e = t.GetEnumerator () + while e.MoveNext () do + match e.Current, error with + | Choice1Of2 v, ValueNone -> yield v + | Choice2Of2 e, ValueNone -> error <- ValueSome e + | Choice2Of2 e, ValueSome x -> error <- ValueSome (combiner x e) + | _ -> () }) + + match error with + | ValueNone -> Choice1Of2 (Array.toSeq res) + | ValueSome e -> Choice2Of2 e + + type Result<'t, 'error> with /// Returns the first Error if it contains an Error element, otherwise a list of all elements @@ -236,3 +253,19 @@ module Extensions = | ValueNone -> Ok (accumulator.Close () |> Array.toSeq) | ValueSome x -> Error x #endif + + /// Returns all Errors combined, otherwise a sequence of all elements. + static member Parallel (combiner, t: seq>) = + let mutable error = ValueNone + let res = Seq.toArray (seq { + use e = t.GetEnumerator () + while e.MoveNext () do + match e.Current, error with + | Ok v , ValueNone -> yield v + | Error e, ValueNone -> error <- ValueSome e + | Error e, ValueSome x -> error <- ValueSome (combiner x e) + | _ -> () }) + + match error with + | ValueNone -> Ok (Array.toSeq res) + | ValueSome e -> Error e diff --git a/src/FSharpPlus/Operators.fs b/src/FSharpPlus/Operators.fs index 6153c7fe4..69c778049 100644 --- a/src/FSharpPlus/Operators.fs +++ b/src/FSharpPlus/Operators.fs @@ -724,6 +724,21 @@ module Operators = let inline sequence (t: '``Traversable<'Functor<'T>>``) : '``Functor<'Traversable<'T>>`` = Sequence.Invoke t + // Traversable (Parallel / Pointwise) + + /// + /// Map each element of a structure to an action, evaluate these actions from left to right, pointwise, or in parallel, and collect the results. + /// + /// Traversable + let inline ptraverse (f: 'T->'``Functor<'U>``) (t: '``Traversable<'T>``) : '``Functor<'Traversable<'U>>`` = ParTraverse.Invoke f t + + /// + /// Evaluate each action in the structure from left to right, pointwise, or in parallel, and collect the results. + /// + /// Traversable + let inline psequence (t: '``Traversable<'Functor<'T>>``) : '``Functor<'Traversable<'T>>`` = ParSequence.Invoke t + + // Bifoldable ///