Skip to content

Commit d6de56b

Browse files
authored
Assert proper non-seq traversals (#594)
1 parent 1166af9 commit d6de56b

File tree

8 files changed

+53
-17
lines changed

8 files changed

+53
-17
lines changed

src/FSharpPlus/Control/Applicative.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ type Apply =
2929
static member ``<*>`` (struct (f: Task<_> , x: Task<'T> ), _output: Task<'U> , [<Optional>]_mthd: Apply) = Task.apply f x : Task<'U>
3030
#endif
3131
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
32-
static member ``<*>`` (struct (f: ValueTask<_> , x: ValueTask<'T> ), _output: ValueTask<'U> , [<Optional>]_mthd: Default2) = ValueTask.apply f x : ValueTask<'U>
32+
static member ``<*>`` (struct (f: ValueTask<_> , x: ValueTask<'T> ), _output: ValueTask<'U> , [<Optional>]_mthd: Apply) : ValueTask<'U> = ValueTask.apply f x
33+
static member ``<*>`` (struct (_: DmStruct1<_> , _: DmStruct1<'T> ), _output: DmStruct1<'U> , [<Optional>]_mthd: Apply) : DmStruct1<'U> = Unchecked.defaultof<DmStruct1<'U>>
3334
#endif
3435
static member ``<*>`` (struct (f: Async<_> , x: Async<'T> ), _output: Async<'U> , [<Optional>]_mthd: Apply) = Async.apply f x : Async<'U>
3536
static member ``<*>`` (struct (f: option<_> , x: option<'T> ), _output: option<'U> , [<Optional>]_mthd: Apply) = Option.apply f x : option<'U>

src/FSharpPlus/Control/Comonad.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ type Extend =
6767
tcs.Task
6868
#endif
6969

70-
#if (!NET45 && !NETSTANDARD2_0) && !FABLE_COMPILER
70+
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
7171
static member (=>>) (g: ValueTask<'T> , f: ValueTask<'T> -> 'U ) : ValueTask<'U> =
7272
if g.IsCompletedSuccessfully then
7373
try

src/FSharpPlus/Control/Monad.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ type Return =
146146
#endif
147147
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
148148
static member Return (_: 'T ValueTask , _: Return ) = fun (x: 'T) -> ValueTask<'T> x : 'T ValueTask
149+
static member Return (_: 'T DmStruct1 , _: Return ) = fun (_: 'T) -> Unchecked.defaultof<_> : 'T DmStruct1
149150
#endif
150151
static member Return (_: option<'a> , _: Return ) = fun x -> Some x : option<'a>
151152
static member Return (_ : voption<'a> , _: Return ) = fun x -> ValueSome x : voption<'a>

src/FSharpPlus/Control/ZipApplicative.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Pure =
3838
#if !FABLE_COMPILER
3939
static member Pure (_: 'T Task , _: Pure) = fun x -> Task.FromResult x : 'T Task
4040
#endif
41-
#if NETSTANDARD2_1 && !FABLE_COMPILER
41+
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
4242
static member Pure (_: 'T ValueTask , _: Pure) = fun (x: 'T) -> ValueTask<'T> x : 'T ValueTask
4343
#endif
4444
static member Pure (x: option<'a> , _: Pure) = Return.Return (x, Unchecked.defaultof<Return>)
@@ -147,7 +147,7 @@ type Map2 =
147147
#if !FABLE_COMPILER
148148
static member Map2 (f, (x: Task<'T> , y: Task<'U> ), _mthd: Map2) = Task.map2 f x y
149149
#endif
150-
#if NETSTANDARD2_1 && !FABLE_COMPILER
150+
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
151151
static member Map2 (f, (x: ValueTask<'T> , y: ValueTask<'U> ), _mthd: Map2) = ValueTask.map2 f x y
152152
#endif
153153
static member Map2 (f, (x , y ), _mthd: Map2) = Async.map2 f x y
@@ -194,7 +194,7 @@ type Map3 =
194194
#if !FABLE_COMPILER
195195
static member Map3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Map3) = Task.map3 f x y z
196196
#endif
197-
#if NETSTANDARD2_1 && !FABLE_COMPILER
197+
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
198198
static member Map3 (f, (x: ValueTask<'T> , y: ValueTask<'U> , z: ValueTask<'V> ), _mthd: Map3) = ValueTask.map3 f x y z
199199
#endif
200200
static member Map3 (f, (x , y , z ), _mthd: Map3) = Async.map3 f x y z

src/FSharpPlus/Internals.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ type Either<'t,'u> =
114114
| Right of 'u
115115

116116
type DmStruct = struct end
117+
type DmStruct1<'T1> = struct end
117118

118119
type KeyValuePair2<'TKey, 'TValue> = struct
119120
val Key : 'TKey

tests/FSharpPlus.Tests/Asyncs.fs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ module Async =
1111
open FSharpPlus
1212
open FSharpPlus.Data
1313
open FSharpPlus.Extensions
14-
open FSharpPlus.Tests.Helpers
1514

1615
exception TestException of string
1716

@@ -21,7 +20,7 @@ module Async =
2120
Task.WaitAny t |> ignore
2221
t
2322

24-
static member WhenAll (source: Async<'T> seq) = source |> Seq.map (fun x -> Async.AsTask x) |> Task.WhenAll |> Async.Await
23+
static member WhenAll (source: Async<'T> seq) = source |> Seq.map Async.AsTask |> Task.WhenAll |> Async.Await
2524

2625

2726
module AsyncTests =
@@ -55,15 +54,15 @@ module Async =
5554
let t12123 = Async.zip3 t12t12 t33 t4
5655
let ac1 =
5756
try
58-
(t12123 |> Async.AsTaskAndWait).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
57+
Async.AsTaskAndWait(t12123).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
5958
with e ->
60-
failwithf "Failure in testAsyncZip. Async status is %A . Exception is %A" (t12123 |> Async.AsTaskAndWait).Status e
59+
failwithf "Failure in testAsyncZip. Async status is %A . Exception is %A" (Async.AsTaskAndWait t12123).Status e
6160

6261
CollectionAssert.AreEquivalent ([1; 2; 1; 2; 3], ac1, "Async.zip(3) should add only non already existing exceptions.")
6362

6463
let t13 = Async.zip3 (Async.zip t1 t3) t4 (Async.zip t5 t6)
65-
Assert.AreEqual (true, (t13 |> Async.AsTaskAndWait).IsFaulted, "Async.zip(3) between a value, an exception and a cancellation -> exception wins.")
66-
let ac2 = (t13 |> Async.AsTaskAndWait).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
64+
Assert.AreEqual (true, Async.AsTaskAndWait(t13).IsFaulted, "Async.zip(3) between a value, an exception and a cancellation -> exception wins.")
65+
let ac2 = Async.AsTaskAndWait(t13).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
6766
CollectionAssert.AreEquivalent ([1; 3], ac2, "Async.zip between 2 exceptions => both exceptions returned, even after combining with cancellation and values.")
6867

6968
[<Test>]
@@ -85,15 +84,27 @@ module Async =
8584
let t12123 = Async.zip3 t12t12 t33 t4
8685
let ac1 =
8786
try
88-
(t12123 |> Async.AsTaskAndWait).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
87+
Async.AsTaskAndWait(t12123).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
8988
with e ->
90-
failwithf "Failure in testAsyncZipAsync. Async status is %A . Exception is %A" (t12123 |> Async.AsTaskAndWait).Status e
89+
failwithf "Failure in testAsyncZipAsync. Async status is %A . Exception is %A" (Async.AsTaskAndWait t12123).Status e
9190

9291
CollectionAssert.AreEquivalent ([1; 2; 1; 2; 3], ac1, "Async.zip(3)Async should add only non already existing exceptions.")
9392

9493
let t13 = Async.zip3 (Async.zip t1 t3) t4 (Async.zip t5 t6)
95-
Assert.AreEqual (true, (t13 |> Async.AsTaskAndWait).IsFaulted, "Async.zip(3)Async between a value, an exception and a cancellation -> exception wins.")
96-
let ac2 = (t13 |> Async.AsTaskAndWait).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
94+
Assert.AreEqual (true, Async.AsTaskAndWait(t13).IsFaulted, "Async.zip(3)Async between a value, an exception and a cancellation -> exception wins.")
95+
let ac2 = Async.AsTaskAndWait(t13).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
9796
CollectionAssert.AreEquivalent ([1; 3], ac2, "Async.zipAsync between 2 exceptions => both exceptions returned, even after combining with cancellation and values.")
9897

98+
[<Test>]
99+
let testAsyncTraversals =
100+
let t1 = createAsync true 20 1
101+
let t2 = createAsync true 10 2
102+
let t3 = createAsync false 30 3
103+
104+
let t123 = Async.map3 (fun x y z -> [x; y; z]) t1 t2 t3
105+
let t123' = transpose [t1; t2; t3]
106+
let t123'' = sequence [t1; t2; t3] : Async<int list>
107+
CollectionAssert.AreEquivalent ((Async.AsTaskAndWait t123).Exception.InnerExceptions, (Async.AsTaskAndWait t123').Exception.InnerExceptions, "Async.map3 (fun x y z -> [x; y; z]) t1 t2 t3 is the same as transpose [t1; t2; t3]")
108+
CollectionAssert.AreNotEquivalent ((Async.AsTaskAndWait t123).Exception.InnerExceptions, (Async.AsTaskAndWait t123'').Exception.InnerExceptions, "Async.map3 (fun x y z -> [x; y; z]) t1 t2 t3 is not the same as sequence [t1; t2; t3]")
109+
99110
#endif

tests/FSharpPlus.Tests/Task.fs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ module Task =
227227
let ac2 = t13.Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
228228
CollectionAssert.AreEquivalent ([1; 3], ac2, "Task.zipAsync between 2 exceptions => both exceptions returned, even after combining with cancellation and values.")
229229

230+
[<Test>]
231+
let testTaskTraversals =
232+
let t1 = createTask true 20 1
233+
let t2 = createTask true 10 2
234+
let t3 = createTask false 30 3
235+
236+
let t123 = Task.map3 (fun x y z -> [x; y; z]) t1 t2 t3
237+
let t123' = transpose [t1; t2; t3]
238+
let t123'' = sequence [t1; t2; t3]
239+
CollectionAssert.AreEquivalent (t123.Exception.InnerExceptions, t123'.Exception.InnerExceptions, "Task.map3 (fun x y z -> [x; y; z]) t1 t2 t3 is the same as transpose [t1; t2; t3]")
240+
CollectionAssert.AreNotEquivalent (t123.Exception.InnerExceptions, t123''.Exception.InnerExceptions, "Task.map3 (fun x y z -> [x; y; z]) t1 t2 t3 is not the same as sequence [t1; t2; t3]")
241+
230242

231243
module TaskBuilderTests =
232244

tests/FSharpPlus.Tests/ValueTask.fs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ module ValueTask =
1919
static member WaitAny (source: ValueTask<'T>) = source.AsTask () |> Task.WaitAny |> ignore
2020

2121
module ValueTask =
22-
open System.Threading
2322

2423
// Following is not available in F#6
2524

@@ -185,5 +184,16 @@ module ValueTask =
185184
Assert.AreEqual (true, t13.IsFaulted, "ValueTask.zip(3)Async between a value, an exception and a cancellation -> exception wins.")
186185
let ac2 = (t13.AsTask ()).Exception.InnerExceptions |> Seq.map (fun x -> int (Char.GetNumericValue x.Message.[35]))
187186
CollectionAssert.AreEquivalent ([1; 3], ac2, "ValueTask.zipAsync between 2 exceptions => both exceptions returned, even after combining with cancellation and values.")
188-
187+
188+
[<Test>]
189+
let testTaskTraversals =
190+
let t1 = createValueTask true 20 1
191+
let t2 = createValueTask true 10 2
192+
let t3 = createValueTask false 30 3
193+
194+
let t123 = ValueTask.map3 (fun x y z -> [x; y; z]) t1 t2 t3
195+
let t123' = transpose [t1; t2; t3]
196+
let t123'' = sequence [t1; t2; t3]
197+
CollectionAssert.AreEquivalent (t123.AsTask().Exception.InnerExceptions, t123'.AsTask().Exception.InnerExceptions, "ValueTask.map3 (fun x y z -> [x; y; z]) t1 t2 t3 is the same as transpose [t1; t2; t3]")
198+
CollectionAssert.AreNotEquivalent (t123.AsTask().Exception.InnerExceptions, t123''.AsTask().Exception.InnerExceptions, "ValueTask.map3 (fun x y z -> [x; y; z]) t1 t2 t3 is not the same as sequence [t1; t2; t3]")
189199
#endif

0 commit comments

Comments
 (0)