diff --git a/docsrc/content/abstraction-foldable.fsx b/docsrc/content/abstraction-foldable.fsx index a7dfe0c1e..db5845b85 100644 --- a/docsrc/content/abstraction-foldable.fsx +++ b/docsrc/content/abstraction-foldable.fsx @@ -59,7 +59,8 @@ From .Net/F# - ``list<'T>`` - ``'T []`` - ``option<'T>`` - - ``voption<'T>`` + - ``voption<'T>`` + - ``Result<'T, 'Error>`` - ``ResizeArray<'T>`` - ``ReadOnlyCollection<'T>`` - ``IReadOnlyCollection<'T>`` diff --git a/src/FSharpPlus/Control/Foldable.fs b/src/FSharpPlus/Control/Foldable.fs index 02b0aa556..9287d375c 100644 --- a/src/FSharpPlus/Control/Foldable.fs +++ b/src/FSharpPlus/Control/Foldable.fs @@ -42,11 +42,18 @@ open FSharpPlus.Internals.Prelude type ToSeq = inherit Default1 - static member ToSeq (x: seq<'T> , []_impl: ToSeq) = x - static member ToSeq (x: Text.StringBuilder, _: ToSeq) = string x :> seq - static member ToSeq (x: string , _: ToSeq) = String.toSeq x - static member ToSeq (x: option<'T>, []_impl: ToSeq) = match x with Some x -> Seq.singleton x | _ -> Seq.empty - static member ToSeq (x: Id<'T> , []_impl: ToSeq) = Seq.singleton x.getValue + static member ToSeq (x: seq<'T>, []_impl: ToSeq) = + #if TEST_TRACE + Traces.add "ToSeq seq" + #endif + x + + static member ToSeq (x: Text.StringBuilder, _: ToSeq) = string x :> seq + static member ToSeq (x: string , _: ToSeq) = String.toSeq x + static member ToSeq (x: option<'T> , []_impl: ToSeq) = match x with Some x -> Seq.singleton x | _ -> Seq.empty + static member ToSeq (x: voption<'T> , []_impl: ToSeq) = match x with ValueSome x -> Seq.singleton x | _ -> Seq.empty + static member ToSeq (x: Result<'T, _>, []_impl: ToSeq) = match x with Ok x -> Seq.singleton x | _ -> Seq.empty + static member ToSeq (x: Id<'T> , []_impl: ToSeq) = Seq.singleton x.getValue static member inline Invoke (source: '``Foldable<'T>``) : seq<'T> = let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member ToSeq : _*_ -> _) b, a) @@ -56,7 +63,13 @@ type ToSeq = static member inline InvokeOnInstance (source: '``Foldable<'T>``) : seq<'T> = (^``Foldable<'T>``: (static member ToSeq : _ -> _) source) type ToSeq with - static member inline ToSeq (x: 'S when 'S :> Collections.IEnumerable, []_impl: Default2) = let _f i x : 'T = (^S : (member get_Item : int -> 'T) x, i) in Seq.cast<'T> x : seq<'T> + static member inline ToSeq (x: 'S when 'S :> Collections.IEnumerable, []_impl: Default2) = + #if TEST_TRACE + Traces.add "ToSeq IEnumerable" + #endif + let _f i x : 'T = (^S : (member get_Item : int -> 'T) x, i) + Seq.cast<'T> x : seq<'T> + static member inline ToSeq (x: 'Foldable , []_impl: Default1) = ToSeq.InvokeOnInstance x static member inline ToSeq (_: 'T when 'T: null and 'T: struct , _: Default1) = () @@ -99,16 +112,22 @@ type ToArray = type FoldBack = inherit Default1 - static member inline FoldBack (x: 'F , f: 'a->'b->'b, z: 'b , []_impl: Default2) = List.foldBack f (ToList.Invoke x) z + static member inline FoldBack (x: 'F, f: 'a -> 'b -> 'b, z: 'b, []_impl: Default2) = + #if TEST_TRACE + Traces.add "Foldback Default" + #endif + List.foldBack f (ToList.Invoke x) z static member inline FoldBack (x: 'F , f: 'a->'b->'b, z: 'b , []_impl: Default1) = (^F : (static member FoldBack : ^F -> _ -> _-> ^b) x, f, z) - static member FoldBack (x: seq<_> , f , z , []_impl: FoldBack) = List.foldBack f (Seq.toList x) z - static member FoldBack (x: option<_> , f , z , []_impl: FoldBack) = match x with Some x -> f x z | _ -> z + static member FoldBack (x: seq<_> , f , z , []_impl: FoldBack) = List.foldBack f (Seq.toList x) z + static member FoldBack (x: option<_> , f , z , []_impl: FoldBack) = match x with Some x -> f x z | _ -> z + static member FoldBack (x: voption<_> , f , z , []_impl: FoldBack) = match x with ValueSome x -> f x z | _ -> z + static member FoldBack (x: Result<_, _> , f , z , []_impl: FoldBack) = match x with Ok x -> f x z | _ -> z static member FoldBack (x: list<_> , f , z , []_impl: FoldBack) = List.foldBack f x z static member FoldBack (x: _ [] , f , z , []_impl: FoldBack) = Array.foldBack f x z static member FoldBack (x: Set<_> , f , z , []_impl: FoldBack) = Set.foldBack f x z static member FoldBack (x: _ ResizeArray, f , z , []_impl: FoldBack) = Array.foldBack f (x.ToArray ()) z static member FoldBack (x: string , f , z , []_impl: FoldBack) = Array.foldBack f (x.ToCharArray ()) z - static member FoldBack (x: StringBuilder, f , z , []_impl: FoldBack) = Array.foldBack f (x.ToString().ToCharArray ()) z + static member FoldBack (x: StringBuilder, f , z , []_impl: FoldBack) = Array.foldBack f (x.ToString().ToCharArray ()) z static member FoldBack (x: Id<'a> , f , z , []_impl: FoldBack) = f x.getValue z static member inline Invoke (folder: 'T->'State->'State) (state: 'State) (foldable: '``Foldable'``) : 'State = @@ -122,10 +141,12 @@ type FoldMap = static member inline FromFoldFoldBack f x = FoldBack.Invoke (Plus.Invoke << f) (Zero.Invoke ()) x - static member inline FoldMap (x: option<_>, f, []_impl: FoldMap ) = match x with Some x -> f x | _ -> Zero.Invoke () - static member inline FoldMap (x: list<_> , f, []_impl: FoldMap ) = List.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x - static member inline FoldMap (x: Set<_> , f, []_impl: FoldMap ) = Seq.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x - static member inline FoldMap (x: _ [] , f, []_impl: FoldMap ) = Array.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x + static member inline FoldMap (x: option<_> , f, []_impl: FoldMap) = match x with Some x -> f x | _ -> Zero.Invoke () + static member inline FoldMap (x: voption<_> , f, []_impl: FoldMap) = match x with ValueSome x -> f x | _ -> Zero.Invoke () + static member inline FoldMap (x: Result<_, _>, f, []_impl: FoldMap) = match x with Ok x -> f x | _ -> Zero.Invoke () + static member inline FoldMap (x: list<_> , f, []_impl: FoldMap) = List.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x + static member inline FoldMap (x: Set<_> , f, []_impl: FoldMap) = Seq.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x + static member inline FoldMap (x: _ [] , f, []_impl: FoldMap) = Array.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x static member inline Invoke (f: 'T->'Monoid) (x: '``Foldable'``) : 'Monoid = let inline call_2 (a: ^a, b: ^b, f) = ((^a or ^b) : (static member FoldMap : _*_*_ -> _) b, f, a) @@ -133,7 +154,11 @@ type FoldMap = call (Unchecked.defaultof, x, f) type FoldMap with - static member inline FoldMap (x: seq<_> , f, []_impl: Default2) = Seq.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x + static member inline FoldMap (x: seq<_> , f, []_impl: Default2) = + #if TEST_TRACE + Traces.add "FoldMap Default" + #endif + Seq.fold (fun x y -> Plus.Invoke x (f y)) (Zero.Invoke ()) x static member inline FoldMap (x , f, []_impl: Default1) = (^F : (static member FoldMap : ^F -> _ -> _) x, f) static member inline FoldMap (_: ^t when ^t: null and ^t: struct, _, _: Default1) = () @@ -146,14 +171,21 @@ type Fold = static member inline FromFoldMap f z t = let (f: _Dual<_Endo<'t>>) = FoldMap.Invoke (_Dual << _Endo << flip f) t in f.Value.Value z - static member inline Fold (x , f, z, []_impl: Default2) = Seq.fold f z (ToSeq.Invoke x) - static member inline Fold (x: 'F , f: 'b->'a->'b, z: 'b, []_impl: Default1) = (^F : (static member Fold : ^F -> _ -> _-> ^b) x, f, z) - static member Fold (x: option<_>, f, z , []_impl: Fold ) = match x with Some x -> f z x | _ -> z - static member Fold (x: Id<_> , f, z , []_impl: Fold ) = f z x.getValue - static member Fold (x: seq<_> , f, z , []_impl: Fold ) = Seq.fold f z x - static member Fold (x: list<_> , f, z , []_impl: Fold ) = List.fold f z x - static member Fold (x: Set<_> , f, z , []_impl: Fold ) = Set.fold f z x - static member Fold (x: _ [] , f, z , []_impl: Fold ) = Array.fold f z x + static member inline Fold (x , f, z, []_impl: Default2) = + #if TEST_TRACE + Traces.add "Fold Default" + #endif + Seq.fold f z (ToSeq.Invoke x) + + static member inline Fold (x: 'F , f: 'b->'a->'b, z: 'b, []_impl: Default1) = (^F : (static member Fold : ^F -> _ -> _-> ^b) x, f, z) + static member Fold (x: option<_> , f, z , []_impl: Fold ) = match x with Some x -> f z x | _ -> z + static member Fold (x: voption<_> , f, z , []_impl: Fold ) = match x with ValueSome x -> f z x | _ -> z + static member Fold (x: Result<_, _>, f, z , []_impl: Fold ) = match x with Ok x -> f z x | _ -> z + static member Fold (x: Id<_> , f, z , []_impl: Fold ) = f z x.getValue + static member Fold (x: seq<_> , f, z , []_impl: Fold ) = Seq.fold f z x + static member Fold (x: list<_> , f, z , []_impl: Fold ) = List.fold f z x + static member Fold (x: Set<_> , f, z , []_impl: Fold ) = Set.fold f z x + static member Fold (x: _ [] , f, z , []_impl: Fold ) = Array.fold f z x static member inline Invoke (folder: 'State->'T->'State) (state: 'State) (foldable: '``Foldable'``) : 'State = let inline call_2 (a: ^a, b: ^b, f, z) = ((^a or ^b) : (static member Fold : _*_*_*_ -> _) b, f, z, a) @@ -170,7 +202,7 @@ type Exists = static member Exists (x: 'a [] , f , []_impl: Exists ) = Array.exists f x static member Exists (x: Set<'a> , f , []_impl: Exists ) = Set.exists f x static member Exists (x: 'a ResizeArray , f , []_impl: Exists ) = Seq.exists f x - static member Exists (x: string , f , []_impl: Exists ) = String.exists f x + static member Exists (x: string , f , []_impl: Exists ) = String.exists f x static member Exists (x: StringBuilder , f , []_impl: Exists ) = x |> string |> String.exists f static member inline Invoke (predicate: 'T->bool) (source: '``Foldable'``) = @@ -231,6 +263,8 @@ type Head = static member inline Head (x: '``Foldable<'T>``, []_impl: Default2) = Seq.head (ToSeq.Invoke x) : 'T static member inline Head (x: '``Foldable<'T>``, []_impl: Default1) = (^``Foldable<'T>`` : (member Head : 'T) x) static member Head (x: 'T option , []_impl: Head ) = x.Value + static member Head (x: 'T voption , []_impl: Head ) = x.Value + static member Head (x: Result<_, _> , []_impl: Head ) = Result.get x static member Head (x: 'T [] , []_impl: Head ) = x.[0] static member Head (x: NonEmptySeq<'T> , []_impl: Head ) = x.First static member Head (x: Id<'T> , []_impl: Head ) = x.getValue @@ -390,6 +424,8 @@ type Length = static member Length (x: ResizeArray<'T> , []_impl: Length ) = x.Count static member Length (x: 'T list , []_impl: Length ) = List.length x static member Length (x: option<'T> , []_impl: Length ) = if x.IsSome then 1 else 0 + static member Length (x: voption<'T> , []_impl: Length ) = if x.IsSome then 1 else 0 + static member Length (x: Result<_, _> , []_impl: Length ) = if Result.isOk x then 1 else 0 static member Length (x: 'T [] , []_impl: Length ) = Array.length x static member inline Invoke (source: '``Foldable<'T>``) = diff --git a/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj b/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj index 734bf6e45..bea71bddc 100644 --- a/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj +++ b/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj @@ -22,6 +22,7 @@ + diff --git a/tests/FSharpPlus.Tests/Folds.fs b/tests/FSharpPlus.Tests/Folds.fs new file mode 100644 index 000000000..3efe4444c --- /dev/null +++ b/tests/FSharpPlus.Tests/Folds.fs @@ -0,0 +1,38 @@ +namespace FSharpPlus.Tests + +#nowarn "686" + +open System +open System.Collections.ObjectModel +open FSharpPlus +open FSharpPlus.Data +open FSharpPlus.Control +open NUnit.Framework +open Helpers +open FSharpPlus.Math.Applicative +open CSharpLib +open System.Threading.Tasks +#if TEST_TRACE +open FSharpPlus.Internals +#endif + +module Folds = + + [] + let basicFolds () = + #if TEST_TRACE + Traces.reset() + #endif + let r1 = set [1..3] |> fold (+) 0 + let r2 = set [1..3] |> toSeq + let r3 = ValueSome 1 |> toSeq + let r4 = ValueSome 1 |> fold (+) 0 + let r5 = Ok 1 |> fold (+) 0 + Assert.AreEqual (6, r1) + CollectionAssert.AreEqual ([1; 2; 3], r2) + CollectionAssert.AreEqual ([1], r3) + Assert.AreEqual (1, r4) + Assert.AreEqual (1, r5) + #if TEST_TRACE + CollectionAssert.AreEqual (["ToSeq seq"], Traces.get()) + #endif \ No newline at end of file