Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions src/FSharpPlus/Control/Functor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,6 @@ type Zip =
static member Zip ((x: Async<'T> , y: Async<'U> , _output: Async<'T*'U> ), _mthd: Zip) = Async.zip x y
#if !FABLE_COMPILER
static member Zip ((x: Task<'T> , y: Task<'U> , _output: Task<'T*'U> ), _mthd: Zip) = Task.zip x y
#endif
#if !FABLE_COMPILER
static member Zip ((x: ValueTask<'T> , y: ValueTask<'U> , _output: ValueTask<'T*'U> ), _mthd: Zip) = ValueTask.zip x y
#endif

Expand All @@ -239,6 +237,43 @@ type Zip with
static member inline Zip ((_: ^t when ^t : null and ^t: struct, _: ^u when ^u : null and ^u: struct, _output: ^r when ^r : null and ^r: struct), _mthd: Default1) = id
static member inline Zip ((x: '``ZipFunctor<'T1>`` , y: '``ZipFunctor<'T2>`` , _output: '``ZipFunctor<'T1 * 'T2>`` ), _mthd: Default1) = Zip.InvokeOnInstance x y : '``ZipFunctor<'T1 * 'T2>``


type Zip3 =
inherit Default1
static member Zip3 ((x: IEnumerator<'T1> , y: IEnumerator<'T2> , z: IEnumerator<'T3> , _output: IEnumerator<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = Enumerator.zip3 x y z
static member Zip3 ((x: seq<'T> , y: seq<'U> , z: seq<'V> , _output: seq<'T*'U*'V> ), _mthd: Zip3) = Seq.zip3 x y z
static member Zip3 ((x: IDictionary<'K, 'T> , y: IDictionary<'K, 'U> , z: IDictionary<'K, 'V> , _output: IDictionary<'K, 'T * 'U * 'V> ), _mthd: Zip3) = Dict.zip3 x y z
static member Zip3 ((x: IReadOnlyDictionary<'K, 'T>, y: IReadOnlyDictionary<'K, 'U>, z: IReadOnlyDictionary<'K, 'V>, _output: IReadOnlyDictionary<'K, 'T * 'U * 'V>), _mthd: Zip3) = IReadOnlyDictionary.zip3 x y z
static member Zip3 ((x: Dictionary<'K, 'T> , y: Dictionary<'K, 'U> , z: Dictionary<'K, 'V> , _output: Dictionary<'K, 'T * 'U * 'V> ), _mthd: Zip3) = Dictionary.zip3 x y z
static member Zip3 ((x: Map<'K, 'T> , y: Map<'K, 'U> , z: Map<'K, 'V> , _output: Map<'K, 'T * 'U * 'V> ), _mthd: Zip3) = Map.zip3 x y z
static member Zip3 ((f: 'R -> 'T1 , g: 'R -> 'T2 , h: 'R -> 'T3 , _output: 'R -> 'T1 * 'T2 * 'T3 ), _mthd: Zip3) = fun r -> (f r, g r, h r)
static member Zip3 ((f: Func<'R, 'T1> , g: Func<'R, 'T2> , h: Func<'R, 'T3> , _output: Func<'R, 'T1 * 'T2 * 'T3> ), _mthd: Zip3) = Func<_,_> (fun r -> (f.Invoke r, g.Invoke r, h.Invoke r))
static member Zip3 ((x: list<'T1> , y: list<'T2> , z: list<'T3> , _output: list<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = List.zip3Shortest x y z
static member Zip3 ((x: 'T1 [] , y: 'T2 [] , z: 'T3 [] , _output: ('T1 * 'T2 * 'T3) [] ), _mthd: Zip3) = Array.zip3Shortest x y z
static member Zip3 ((x: ResizeArray<'T1> , y: ResizeArray<'T2> , z: ResizeArray<'T3> , _output: ResizeArray<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = ResizeArray.zip3Shortest x y z
static member Zip3 ((x: option<'T1> , y: option<'T2> , z: option<'T3> , _output: option<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = Option.zip3 x y z
static member Zip3 ((x: voption<'T1> , y: voption<'T2> , z: voption<'T3> , _output: voption<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = ValueOption.zip3 x y z
static member inline Zip3 ((x: Result<'T, 'Error> , y: Result<'U, 'Error> , z: Result<'V, 'Error> , _output: Result<'T * 'U * 'V, 'Error> ), _mthd: Zip3) = Result.apply3With Plus.Invoke (fun a b c -> a, b, c) x y z
static member inline Zip3 ((x: Choice<'T, 'Error> , y: Choice<'U, 'Error> , z: Choice<'V, 'Error> , _output: Choice<'T * 'U * 'V, 'Error> ), _mthd: Zip3) = Choice.apply3With Plus.Invoke (fun a b c -> a, b, c) x y z
static member Zip3 ((x: Async<'T1> , y: Async<'T2> , z: Async<'T3> , _output: Async<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = Async.zip3 x y z
#if !FABLE_COMPILER
static member Zip3 ((x: Task<'T1> , y: Task<'T2> , z: Task<'T3> , _output: Task<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = Task.zip3 x y z
static member Zip3 ((x: ValueTask<'T1> , y: ValueTask<'T2> , z: ValueTask<'T3> , _output: ValueTask<'T1 * 'T2 * 'T3> ), _mthd: Zip3) = ValueTask.zip3 x y z
#endif

static member inline Invoke (source1: '``ZipFunctor<'T1>``) (source2: '``ZipFunctor<'T2>``) (source3: '``ZipFunctor<'T3>``) =
let inline call_5 (a: ^a, b: ^b, c: ^c, d: ^d, e: ^e) = ((^a or ^b or ^c or ^d or ^e) : (static member Zip3 : (_*_*_*_)*_ -> _) (b, c, d, e), a)
let inline call (a: 'a, b: 'b, c: 'c, d: 'd) = call_5 (a, b, c, d, Unchecked.defaultof<'r>) : 'r
call (Unchecked.defaultof<Zip3>, source1, source2, source3) : '``ZipFunctor<'T1 * 'T2 * 'T3>``

static member inline InvokeOnInstance (source1: '``ZipFunctor<'T1>``) (source2: '``ZipFunctor<'T2>``) (source3: '``ZipFunctor<'T3>``) : '``ZipFunctor<'T1 * 'T2 * 'T3>`` =
((^``ZipFunctor<'T1>`` or ^``ZipFunctor<'T2>`` or ^``ZipFunctor<'T3>`` or ^``ZipFunctor<'T1 * 'T2 * 'T3>``) : (static member Zip3 : _*_*_ -> _) source1, source2, source3)

type Zip3 with
static member inline Zip3 ((_: ^t when ^t : null and ^t: struct, _: ^u when ^u : null and ^u: struct, _: ^v when ^v : null and ^v: struct, _output: ^r when ^r : null and ^r: struct), _mthd: Default1) = id
static member inline Zip3 ((x: '``ZipFunctor<'T1>`` , y: '``ZipFunctor<'T2>`` , z: '``ZipFunctor<'T3>`` , _output: '``ZipFunctor<'T1 * 'T2 * 'T3>``), _mthd: Default1) = Zip3.InvokeOnInstance x y z : '``ZipFunctor<'T1 * 'T2 * 'T3>``


#endif
#if !FABLE_COMPILER

Expand Down
13 changes: 13 additions & 0 deletions src/FSharpPlus/Data/NonEmptyList.fs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ module NonEmptyList =
/// <returns>List with corresponding pairs of input lists.</returns>
let zipShortest (list1: NonEmptyList<'T>) (list2: NonEmptyList<'U>) =
{ Head = (list1.Head, list2.Head); Tail = List.zipShortest list1.Tail list2.Tail }

/// <summary>
/// Zip safely three lists. If one list is shorter, excess elements are discarded from the right end of the longer list.
/// </summary>
/// <param name="list1">First input list.</param>
/// <param name="list2">Second input list.</param>
/// <param name="list3">Third input list.</param>
/// <returns>List with corresponding triplets of input lists.</returns>
let zip3Shortest (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) (list3: NonEmptyList<'T3>) =
{ Head = (list1.Head, list2.Head, list3.Head); Tail = List.zip3Shortest list1.Tail list2.Tail list3.Tail }

/// <summary>Adds an element to the beginning of the given list</summary>
/// <param name="value">The element to add</param>
Expand Down Expand Up @@ -1013,6 +1023,9 @@ type NonEmptyList<'t> with

[<EditorBrowsable(EditorBrowsableState.Never)>]
static member Zip (x, y) = NonEmptyList.zipShortest x y

[<EditorBrowsable(EditorBrowsableState.Never)>]
static member Zip3 (x, y, z) = NonEmptyList.zip3Shortest x y z

static member (>>=) ({Head = x; Tail = xs}, f: _->NonEmptyList<'b>) =
let {Head = y; Tail = ys} = f x
Expand Down
9 changes: 9 additions & 0 deletions src/FSharpPlus/Operators.fs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ module Operators =
/// <category index="1">Functor</category>
let inline zip (source1: '``ZipFunctor<'T1>``) (source2: '``ZipFunctor<'T2>``) : '``ZipFunctor<'T1 * 'T2>`` = Zip.Invoke source1 source2

/// <summary>
/// Zips (tuple) three functors.
/// </summary>
/// <remarks>
/// For collections, if one collection is shorter, excess elements are discarded from the right end of the longer collection.
/// </remarks>
/// <category index="1">Functor</category>
let inline zip3 (source1: '``ZipFunctor<'T1>``) (source2: '``ZipFunctor<'T2>``) (source3: '``ZipFunctor<'T3>``) : '``ZipFunctor<'T1 * 'T2 * 'T3>`` = Zip3.Invoke source1 source2 source3

// Applicative ------------------------------------------------------------

/// <summary>
Expand Down
57 changes: 57 additions & 0 deletions tests/FSharpPlus.Tests/Applicatives.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

open System
open System.Collections.ObjectModel
open System.Threading.Tasks
open FSharpPlus
open FSharpPlus.Data
open NUnit.Framework
Expand Down Expand Up @@ -33,3 +34,59 @@ module Applicatives =

let arr3 = (+) <!> Compose (async { return [|1;2;3|] } ) <.> Compose (async { return [|10;20;30|] })
CollectionAssert.AreEqual ([|11; 22; 33|], arr3 |> Compose.run |> Async.RunSynchronously)


[<Test>]
let zip3Test () =

SideEffects.reset ()
let _a = zip3 (seq [1;2;3]) (seq [1. .. 3. ]) (seq ['a';'b';'c'])
Assert.AreEqual (list<string>.Empty, SideEffects.get ())

let _b = zip3 (dict [1,'1' ; 2,'2' ; 4,'4']) (dict [1,'1' ; 2,'2' ; 3,'3']) (dict [1,'a' ; 2,'b' ; 3,'c'])
let _c = zip3 [ 1;2;3 ] [ 1. .. 3. ] ['a';'b';'c']
let _d = zip3 [|1;2;3|] [|1. .. 3.|] [|'a';'b';'c'|]
let _e = zip3 (async {return 1}) (async {return '2'}) (async {return 3.})
let _f = zip3 (Task.FromResult 1) (Task.FromResult '2') (Task.FromResult 3.)
let _g = zip3 (Some 1) (Some '2') (Some 3.)
let _h = zip3 (Ok 1) (if true then Ok '2' else Error "No") (Ok 3.)

let _fa a = zip3 a (seq [1. .. 3. ]) (seq ['a';'b';'c'])
let _fb a = zip3 a [ 1. .. 3. ] ['a';'b';'c']
let _fc a = zip3 a [|1. .. 3.|] [|'a';'b';'c'|]
let _fd a = zip3 a (async {return '2'}) (async {return 3.})
let _fe a = zip3 a (Task.FromResult '2') (Task.FromResult 3.)

let _ga b = zip3 (seq [1;2;3]) b (seq ['a';'b';'c'])
let _gb b = zip3 [ 1;2;3 ] b ['a';'b';'c']
let _gc b = zip3 [|1;2;3|] b [|'a';'b';'c'|]
let _gd b = zip3 (async {return 1}) b (async {return 3.})
let _ge b = zip3 (Task.FromResult 1) b (Task.FromResult 3.)

let _ha c = zip3 (seq [1;2;3]) (seq [1. .. 3. ]) c
let _hb c = zip3 [ 1;2;3 ] [ 1. .. 3. ] c
let _hc c = zip3 [|1;2;3|] [|1. .. 3.|] c
let _hd c = zip3 (async {return 1}) (async {return '2'}) c
let _he c = zip3 (Task.FromResult 1) (Task.FromResult '2') c

let _ia : _ -> _ -> _ -> _ seq = zip3
let _ib : _ -> _ -> _ -> _ list = zip3
let _ic : _ -> _ -> _ -> _ [] = zip3
let _id : _ -> _ -> _ -> Async<_> = zip3
let _ie : _ -> _ -> _ -> Task<_> = zip3

()

[<Test>]
let genericZip3Shortest () =
let a = zip3 [|1; 2; 3|] [|"a"; "b"|] [|10.; 20.; 30.|]
CollectionAssert.AreEqual ([|1,"a",10.; 2,"b",20.|], a)

let l = zip3 [1; 2] ["a"; "b"; "c"] [10.; 20.; 30.]
CollectionAssert.AreEqual ([1,"a",10.; 2,"b",20.], l)

let e = zip3 (ResizeArray [1; 2]) (ResizeArray ["a"; "b"; "c"]) (ResizeArray [10.; 20.])
CollectionAssert.AreEqual (ResizeArray [1,"a",10.; 2,"b",20.], e)

let nel = zip3 (NonEmptyList.ofList [1; 2]) (NonEmptyList.ofList ["a"; "b"; "c"]) (NonEmptyList.ofList [10.; 20.; 30.])
CollectionAssert.AreEqual (NonEmptyList.ofList [1,"a",10.; 2,"b",20.], nel)
56 changes: 0 additions & 56 deletions tests/FSharpPlus.Tests/General.fs
Original file line number Diff line number Diff line change
Expand Up @@ -67,62 +67,6 @@ type WrappedListC<'s> = WrappedListC of 's list with
static member Zero = WrappedListC List.empty
static member Sum (lst: seq<WrappedListC<_>>) = Seq.head lst

type WrappedListD<'s> = WrappedListD of 's list with
interface Collections.Generic.IEnumerable<'s> with member x.GetEnumerator () = (let (WrappedListD x) = x in x :> _ seq).GetEnumerator ()
interface Collections.IEnumerable with member x.GetEnumerator () = (let (WrappedListD x) = x in x :> _ seq).GetEnumerator () :> Collections.IEnumerator
static member Return (x) = SideEffects.add "Using WrappedListD's Return"; WrappedListD [x]
static member (>>=) ((WrappedListD x):WrappedListD<'T>, f) = SideEffects.add "Using WrappedListD's Bind"; WrappedListD (List.collect (f >> (fun (WrappedListD x) -> x)) x)
static member inline FoldMap (WrappedListD x, f) =
SideEffects.add "Using optimized foldMap"
Seq.fold (fun x y -> x ++ (f y)) zero x
static member Zip (WrappedListD x, WrappedListD y) = SideEffects.add "Using WrappedListD's zip"; WrappedListD (List.zip x y)
static member Exists (x, f) =
SideEffects.add "Using WrappedListD's Exists"
let (WrappedListD lst) = x
List.exists f lst
static member Pick (x, f) =
SideEffects.add "Using WrappedListD's Pick"
let (WrappedListD lst) = x
List.pick f lst
static member Min x =
SideEffects.add "Using WrappedListD's Min"
let (WrappedListD lst) = x
List.min lst
static member MaxBy (x, f) =
SideEffects.add "Using WrappedListD's MaxBy"
let (WrappedListD lst) = x
List.maxBy f lst
static member MapIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's MapIndexed"
WrappedListD (List.mapi f x)
static member ChooseIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's ChooseIndexed"
WrappedListD (List.choosei f x)
static member Lift3 (f, WrappedListD x, WrappedListD y, WrappedListD z) =
SideEffects.add "Using WrappedListD's Lift3"
WrappedListD (List.lift3 f x y z)
static member IterateIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's IterateIndexed"
List.iteri f x
static member inline FoldIndexed (WrappedListD x, f, z) =
SideEffects.add "Using WrappedListD's FoldIndexed"
foldi f z x
static member inline TraverseIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's TraverseIndexed"
WrappedListD <!> (traversei f x : ^r)
static member FindIndex (WrappedListD x, y) =
SideEffects.add "Using WrappedListD's FindIndex"
findIndex y x
static member FindSliceIndex (WrappedListD x, WrappedListD y) =
SideEffects.add "Using WrappedListD's FindSliceIndex"
findSliceIndex y x
static member FindLastSliceIndex (WrappedListD x, WrappedListD y) =
SideEffects.add "Using WrappedListD's FindLastSliceIndex"
findLastSliceIndex y x
member this.Length =
SideEffects.add "Using WrappedListD's Length"
let (WrappedListD lst) = this
List.length lst
type WrappedListE<'s> = WrappedListE of 's list with
static member Return x = WrappedListE [x]
static member (>>=) (WrappedListE x: WrappedListE<'T>, f) = WrappedListE (List.collect (f >> (fun (WrappedListE x) -> x)) x)
Expand Down
63 changes: 62 additions & 1 deletion tests/FSharpPlus.Tests/Helpers.fs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
module FSharpPlus.Tests.Helpers

open NUnit.Framework
open System
open System.Collections
open NUnit.Framework
open FSharpPlus
open FSharpPlus.Control

let areEqual (x:'t) (y:'t) = Assert.AreEqual (x, y)
let areStEqual x y = Assert.IsTrue( (x = y), sprintf "Expected %A to be structurally equal to %A" x y)
Expand All @@ -14,3 +17,61 @@ module SideEffects =
let add x = effects.Add (x)
let get () = effects |> Seq.toList
let are lst = areEquivalent lst (get ())


type WrappedListD<'s> = WrappedListD of 's list with
interface Collections.Generic.IEnumerable<'s> with member x.GetEnumerator () = (let (WrappedListD x) = x in x :> _ seq).GetEnumerator ()
interface Collections.IEnumerable with member x.GetEnumerator () = (let (WrappedListD x) = x in x :> _ seq).GetEnumerator () :> Collections.IEnumerator
static member Return (x) = SideEffects.add "Using WrappedListD's Return"; WrappedListD [x]
static member (>>=) ((WrappedListD x):WrappedListD<'T>, f) = SideEffects.add "Using WrappedListD's Bind"; WrappedListD (List.collect (f >> (fun (WrappedListD x) -> x)) x)
static member inline FoldMap (WrappedListD x, f) =
SideEffects.add "Using optimized foldMap"
Seq.fold (fun x y -> x ++ (f y)) zero x
static member Zip (WrappedListD x, WrappedListD y) = SideEffects.add "Using WrappedListD's zip"; WrappedListD (List.zip x y)
static member Exists (x, f) =
SideEffects.add "Using WrappedListD's Exists"
let (WrappedListD lst) = x
List.exists f lst
static member Pick (x, f) =
SideEffects.add "Using WrappedListD's Pick"
let (WrappedListD lst) = x
List.pick f lst
static member Min x =
SideEffects.add "Using WrappedListD's Min"
let (WrappedListD lst) = x
List.min lst
static member MaxBy (x, f) =
SideEffects.add "Using WrappedListD's MaxBy"
let (WrappedListD lst) = x
List.maxBy f lst
static member MapIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's MapIndexed"
WrappedListD (List.mapi f x)
static member ChooseIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's ChooseIndexed"
WrappedListD (List.choosei f x)
static member Lift3 (f, WrappedListD x, WrappedListD y, WrappedListD z) =
SideEffects.add "Using WrappedListD's Lift3"
WrappedListD (List.lift3 f x y z)
static member IterateIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's IterateIndexed"
List.iteri f x
static member inline FoldIndexed (WrappedListD x, f, z) =
SideEffects.add "Using WrappedListD's FoldIndexed"
foldi f z x
static member inline TraverseIndexed (WrappedListD x, f) =
SideEffects.add "Using WrappedListD's TraverseIndexed"
WrappedListD <!> (traversei f x : ^r)
static member FindIndex (WrappedListD x, y) =
SideEffects.add "Using WrappedListD's FindIndex"
findIndex y x
static member FindSliceIndex (WrappedListD x, WrappedListD y) =
SideEffects.add "Using WrappedListD's FindSliceIndex"
findSliceIndex y x
static member FindLastSliceIndex (WrappedListD x, WrappedListD y) =
SideEffects.add "Using WrappedListD's FindLastSliceIndex"
findLastSliceIndex y x
member this.Length =
SideEffects.add "Using WrappedListD's Length"
let (WrappedListD lst) = this
List.length lst
Loading