Skip to content

Commit 30d990e

Browse files
authored
Non sequential Applicatives (#559)
1 parent 06f8d2b commit 30d990e

28 files changed

+1126
-75
lines changed

docsrc/content/abstraction-traversable.fsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,20 @@ Minimal complete definition
2121
* ``traverse f x`` | ``sequence x``
2222
*)
2323
(**
24-
static member Traverse (t:'Traversable<'T>, f: 'T -> 'Functor<'U>) : 'Functor<'Traversable<'U>>
25-
static member Sequence (t:'Traversable<'Functor<'T>>) : 'Functor<'Traversable<'T>>
24+
static member Traverse (t: 'Traversable<'T>, f: 'T -> 'Applicative<'U>) : 'Applicative<'Traversable<'U>>
25+
static member Sequence (t: 'Traversable<'Applicative<'T>>) : 'Applicative<'Traversable<'T>>
26+
*)
27+
(**
28+
29+
30+
Other operations
31+
----------------
32+
33+
* ``gather f x`` | ``transpose x`` (same as traverse and sequence but operating on ZipApplicatives)
34+
*)
35+
(**
36+
static member Gather (t: 'Traversable<'T>, f: 'T -> 'ZipApplicative<'U>) : 'ZipApplicative<'Traversable<'U>>
37+
static member Transpose (t: 'Traversable<'ZipApplicative<'T>>) : 'ZipApplicative<'Traversable<'T>>
2638
*)
2739
(**
2840
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
(*** hide ***)
2+
// This block of code is omitted in the generated HTML documentation. Use
3+
// it to define helpers that you do not want to show in the documentation.
4+
#r @"../../src/FSharpPlus/bin/Release/netstandard2.0/FSharpPlus.dll"
5+
6+
(**
7+
ZipApplicative
8+
==============
9+
A functor with application, providing operations to embed pure expressions (``pur``), run computations pointwise and/or paralell and combine their results (``<.>``).
10+
___
11+
Minimal complete definition
12+
---------------------------
13+
* ``pur x`` &nbsp; . &nbsp; ``result x``
14+
* ``(<.>) f x``
15+
*)
16+
(**
17+
static member Pure (x: 'T) : 'ZipApplicative<'T>
18+
static member (<.>) (f: 'ZipApplicative<'T -> 'U>, x: 'ZipApplicative<'T>) : 'ZipApplicative<'U>
19+
*)
20+
(**
21+
22+
23+
Other operations
24+
----------------
25+
26+
* ``zip``
27+
*)
28+
(**
29+
static member Zip (x1: 'ZipApplicative<'T1>, x2: 'ZipApplicative<'T2>) : 'ZipApplicative<'T1 * 'T2>
30+
*)
31+
(**
32+
* ``map2``
33+
*)
34+
(**
35+
static member Map2 (f: 'T1 -> 'T2 -> 'T, x1: 'ZipApplicative<'T1>, x2: 'ZipApplicative<'T2>) : 'ZipApplicative<'T>
36+
*)
37+
38+
(**
39+
* ``map3``
40+
*)
41+
(**
42+
static member Map3 (f: 'T1 -> 'T2 -> 'T3 -> 'T, x1: 'ZipApplicative<'T1>, x2: 'ZipApplicative<'T2>, x3: 'ZipApplicative<'T3>) : 'ZipApplicative<'T>
43+
*)
44+
45+
(**
46+
47+
48+
Rules
49+
-----
50+
*)
51+
(**
52+
pur id <.> v = v
53+
pur (<<) <.> u <.> v <.> w = u <.> (v <.> w)
54+
pur f <*> pur x = pur (f x)
55+
u <*> pur y = pur ((|>) y) <.> u
56+
*)
57+
(**
58+
Related Abstractions
59+
--------------------
60+
- [Functor](abstraction-functor.html): A zipApplicative is a functor whose ``map`` operation can be splitted in ``pur`` and ``(<.>)`` operations,
61+
62+
- [ZipApplicative](abstraction-applicative.html) : ZipApplicatives are applicatives which usually don't form a [Monad](abstraction-monad.html).
63+
64+
Concrete implementations
65+
------------------------
66+
From F#
67+
68+
- ``seq<'T>``
69+
- ``list<'T>``
70+
- ``option<'T>`` *
71+
- ``voption<'T>`` *
72+
- ``Lazy<'T>`` *
73+
- ``Async<'T>``
74+
- ``Result<'T, 'U>``
75+
- ``Choice<'T, 'U>``
76+
- ``KeyValuePair<'Key, 'T>`` *
77+
- ``'Monoid * 'T`` *
78+
- ``ValueTuple<'Monoid, 'T>`` *
79+
- ``Task<'T>``
80+
- ``ValueTask<'T>``
81+
- ``'R -> 'T`` *
82+
- ``Expr<'T>`` *
83+
84+
85+
From F#+
86+
87+
- [``NonEmptySeq<'T>``]
88+
- [``NonEmptyList<'T>``](type-nonempty.html)
89+
- [``Compose<'ZipApplicative1<'ZipApplicative2<'T>>>``](type-compose.html)
90+
91+
(*) The operation is the same as that for the normal applicative
92+
93+
94+
Only for <*> operation:
95+
- ``array<'T>``
96+
- ``ResizeArray<'T>``
97+
- ``Map<'Key, 'T>``
98+
- ``Dictionary<'Key, 'T>``
99+
- ``IDictionary<'Key, 'T>``
100+
- ``IReadOnlyDictionary<'Key, 'T>``
101+
102+
103+
[Suggest another](https://github.com/fsprojects/FSharpPlus/issues/new) concrete implementation
104+
105+
Examples
106+
--------
107+
*)
108+
109+
110+
(**
111+
```f#
112+
#r @"nuget: FSharpPlus"
113+
```
114+
*)
115+
116+
open FSharpPlus
117+
118+
119+
// pointwise operations
120+
121+
let arr1 = (+) <!> [|1;2;3|] <*> [|10;20;30|]
122+
let arr2 = (+) <!> [|1;2;3|] <.> [|10;20;30|]
123+
124+
// val arr1: int array = [|11; 21; 31; 12; 22; 32; 13; 23; 33|]
125+
// val arr2: int array = [|11; 22; 33|]
126+
127+
128+
// Validations
129+
130+
let validated = app2 {
131+
let! x = async { return Ok 1 }
132+
and! y = async { return Ok 2 }
133+
and! z = async { return Error ["Error"] }
134+
return x + y + z
135+
}
136+
137+
validated |> Async.RunSynchronously
138+
// val it: Result<int,string list> = Error ["Error"]

src/FSharpPlus/Builders.fs

+28-3
Original file line numberDiff line numberDiff line change
@@ -210,20 +210,45 @@ module GenericBuilders =
210210
member _.Run x : '``Applicative1<Applicative2<Applicative3<'T>>>`` = x
211211

212212

213+
/// Generic ZipApplicative CE builder.
214+
type ZipApplicativeBuilder<'``applicative<'t>``> () =
215+
member _.ReturnFrom (expr) = expr : '``applicative<'t>``
216+
member inline _.Return (x: 'T) = pur x : '``Applicative<'T>``
217+
member inline _.Yield (x: 'T) = pur x : '``Applicative<'T>``
218+
member inline _.BindReturn(x, [<InlineIfLambda>]f) = map f x : '``Applicative<'U>``
219+
member inline _.MergeSources (t1: '``Applicative<'T>``, t2: '``Applicative<'U>``) : '``Applicative<'T * 'U>`` = map2 tuple2 t1 t2
220+
member inline _.MergeSources3 (t1: '``Applicative<'T>``, t2: '``Applicative<'U>``, t3: '``Applicative<'V>``) : '``Applicative<'T * 'U * 'V>`` = map3 tuple3 t1 t2 t3
221+
member _.Run f : '``Applicative<'T>`` = f
222+
223+
/// Generic 2 layers ZipApplicative CE builder.
224+
type ZipApplicativeBuilder2<'``applicative1<applicative2<'t>>``> () =
225+
member _.ReturnFrom expr : '``applicative1<applicative2<'t>>`` = expr
226+
member inline _.Return (x: 'T) : '``Applicative1<Applicative2<'T>>`` = (pur >> pur) x
227+
member inline _.Yield (x: 'T) : '``Applicative1<Applicative2<'T>>`` = (pur >> pur) x
228+
member inline _.BindReturn (x: '``Applicative1<Applicative2<'T>>``, [<InlineIfLambda>]f: _ -> _) : '``Applicative1<Applicative2<'U>>`` = (map >> map) f x
229+
member inline _.MergeSources (t1, t2) : '``Applicative1<Applicative2<'T>>`` = (map2 >> map2) tuple2 t1 t2
230+
member inline _.MergeSources3 (t1, t2, t3) : '``Applicative1<Applicative2<'T>>`` = (map3 >> map3) tuple3 t1 t2 t3
231+
member _.Run x : '``Applicative1<Applicative2<'T>>`` = x
213232

214233
/// Creates a (lazy) monadic computation expression with side-effects (see http://fsprojects.github.io/FSharpPlus/computation-expressions.html for more information)
215234
let monad<'``monad<'t>``> = new MonadFxBuilder<'``monad<'t>``> ()
216235

217236
/// Creates a strict monadic computation expression with side-effects (see http://fsprojects.github.io/FSharpPlus/computation-expressions.html for more information)
218237
let monad'<'``monad<'t>``> = new MonadFxStrictBuilder<'``monad<'t>``> ()
219238

220-
/// Creates an applicative computation expression.
239+
/// Creates a (sequential) applicative computation expression.
221240
let applicative<'``Applicative<'T>``> = ApplicativeBuilder<'``Applicative<'T>``> ()
222241

223-
/// Creates an applicative computation expression which compose effects of two Applicatives.
242+
/// Creates a (sequential) applicative computation expression which compose effects of two Applicatives.
224243
let applicative2<'``Applicative1<Applicative2<'T>>``> = ApplicativeBuilder2<'``Applicative1<Applicative2<'T>>``> ()
225244

226-
/// Creates an applicative computation expression which compose effects of three Applicatives.
245+
/// Creates a (sequential) applicative computation expression which compose effects of three Applicatives.
227246
let applicative3<'``Applicative1<Applicative2<Applicative3<'T>>>``> = ApplicativeBuilder3<'``Applicative1<Applicative2<Applicative3<'T>>>``> ()
228247

248+
/// Creates a (non sequential) applicative computation expression.
249+
let app<'``ZipApplicative<'T>``> = ZipApplicativeBuilder<'``ZipApplicative<'T>``> ()
250+
251+
/// Creates a (non sequential) applicative computation expression which compose effects of two Applicatives.
252+
let app2<'``ZipApplicative1<ZipApplicative2<'T>>``> = ZipApplicativeBuilder2<'``ZipApplicative1<ZipApplicative2<'T>>``> ()
253+
229254
#endif

src/FSharpPlus/Control/Applicative.fs

+6-6
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@ type Lift2 =
109109
static member inline Lift2 (f, ((a: 'Monoid, x: 'T) , (b: 'Monoid, y: 'U) ), _mthd: Lift2) = Plus.Invoke a b, f x y
110110
static member inline Lift2 (f, (struct (a: 'Monoid, x: 'T), struct (b: 'Monoid, y: 'U)), _mthd: Lift2) = struct (Plus.Invoke a b, f x y)
111111
#if !FABLE_COMPILER
112-
static member Lift2 (f, (x: Task<'T> , y: Task<'U> ), _mthd: Lift2) = Task.map2 f x y
112+
static member Lift2 (f, (x: Task<'T> , y: Task<'U> ), _mthd: Lift2) = Task.lift2 f x y
113113
#endif
114114
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
115-
static member Lift2 (f, (x: ValueTask<'T> , y: ValueTask<'U> ), _mthd: Lift2) = ValueTask.map2 f x y
115+
static member Lift2 (f, (x: ValueTask<'T> , y: ValueTask<'U> ), _mthd: Lift2) = ValueTask.lift2 f x y
116116
#endif
117-
static member Lift2 (f, (x , y ), _mthd: Lift2) = Async.map2 f x y
117+
static member Lift2 (f, (x , y ), _mthd: Lift2) = Async.lift2 f x y
118118
static member Lift2 (f, (x , y ), _mthd: Lift2) = Option.map2 f x y
119119

120120
#if !FABLE_COMPILER
@@ -158,12 +158,12 @@ type Lift3 =
158158
static member inline Lift3 (f, ((a: 'Monoid, x: 'T) , (b: 'Monoid, y: 'U) , (c: 'Monoid, z: 'U) ), _mthd: Lift3) = Plus.Invoke (Plus.Invoke a b) c, f x y z
159159
static member inline Lift3 (f, (struct (a: 'Monoid, x: 'T), struct (b: 'Monoid, y: 'U), struct (c: 'Monoid, z: 'U)), _mthd: Lift3) = struct (Plus.Invoke (Plus.Invoke a b) c, f x y z)
160160
#if !FABLE_COMPILER
161-
static member Lift3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Lift3) = Task.map3 f x y z
161+
static member Lift3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Lift3) = Task.lift3 f x y z
162162
#endif
163163
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
164-
static member Lift3 (f, (x: ValueTask<'T> , y: ValueTask<'U> , z: ValueTask<'V> ), _mthd: Lift3) = ValueTask.map3 f x y z
164+
static member Lift3 (f, (x: ValueTask<'T> , y: ValueTask<'U> , z: ValueTask<'V> ), _mthd: Lift3) = ValueTask.lift3 f x y z
165165
#endif
166-
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Async.map3 f x y z
166+
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Async.lift3 f x y z
167167
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Option.map3 f x y z
168168

169169
#if !FABLE_COMPILER

src/FSharpPlus/Control/Functor.fs

+3-2
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,9 @@ type Zip =
217217
static member Zip ((x: 'T [] , y: 'U [] , _output: ('T*'U) [] ), _mthd: Zip) = Array.zipShortest x y
218218
static member Zip ((x: ResizeArray<'T> , y: ResizeArray<'U> , _output: ResizeArray<'T*'U> ), _mthd: Zip) = ResizeArray.zipShortest x y
219219
static member Zip ((x: option<'T> , y: option<'U> , _output: option<'T*'U> ), _mthd: Zip) = Option.zip x y
220-
static member Zip ((x: voption<'T> , y: voption<'U> , _output: voption<'T*'U> ), _mthd: Zip) = ValueOption.zip x y
221-
static member Zip ((x: Result<'T, 'Error> , y: Result<'U, 'Error> , _output: Result<'T * 'U, 'Error> ), _mthd: Zip) = Result.zip x y
220+
static member Zip ((x: voption<'T> , y: voption<'U> , _output: voption<'T*'U> ), _mthd: Zip) = ValueOption.zip x y
221+
static member inline Zip ((x: Result<'T, 'Error> , y: Result<'U, 'Error> , _output: Result<'T * 'U, 'Error> ), _mthd: Zip) = Result.apply2With Plus.Invoke (fun a b -> a, b) x y
222+
static member inline Zip ((x: Choice<'T, 'Error> , y: Choice<'U, 'Error> , _output: Choice<'T * 'U, 'Error> ), _mthd: Zip) = Choice.apply2With Plus.Invoke (fun a b -> a, b) x y
222223
static member Zip ((x: Async<'T> , y: Async<'U> , _output: Async<'T*'U> ), _mthd: Zip) = Async.zip x y
223224
#if !FABLE_COMPILER
224225
static member Zip ((x: Task<'T> , y: Task<'U> , _output: Task<'T*'U> ), _mthd: Zip) = Task.zip x y

src/FSharpPlus/Control/MonadOps.fs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ module internal MonadOps =
88
let inline (>>=) x f = Bind.Invoke x f
99
let inline result x = Return.Invoke x
1010
let inline (<*>) f x = Apply.Invoke f x
11+
let inline pur x = Pure.Invoke x
12+
let inline (<.>) f x = ZipApply.Invoke f x
1113
let inline (<|>) x y = Append.Invoke x y
1214
let inline (>=>) (f: 'a->'``Monad<'b>``) (g: 'b->'``Monad<'c>``) (x: 'a) : '``Monad<'c>`` = f x >>= g
1315

src/FSharpPlus/Control/Monoid.fs

+7-5
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ type Plus =
3434
static member ``+`` (x: AggregateException, y: AggregateException, [<Optional>]_mthd: Plus ) = new AggregateException (seq {yield! x.InnerExceptions; yield! y.InnerExceptions})
3535
static member ``+`` (x: exn , y: exn , [<Optional>]_mthd: Plus ) =
3636
let f (e: exn) = match e with :? AggregateException as a -> a.InnerExceptions :> seq<_> | _ -> Seq.singleton e
37-
new AggregateException (seq {yield! f x; yield! f y}) :> exn
37+
let left = f x
38+
new AggregateException (seq { yield! left; yield! Seq.except left (f y) }) :> exn
3839
#else
3940
static member ``+`` (x: StringBuilder , y: StringBuilder , [<Optional>]_mthd: Plus ) = StringBuilder().Append(string x).Append(string y)
4041
static member ``+`` (_: Id0 , _: Id0 , [<Optional>]_mthd: Plus ) = Id0 ""
4142
static member ``+`` (x: exn , y: exn , [<Optional>]_mthd: Plus ) : exn =
4243
let f (e: exn) = match e with :? AggregateException as a -> a.Data0 :> seq<_> | _ -> Seq.singleton e
43-
AggregateException (seq {yield! f x; yield! f y})
44+
let left = f x
45+
AggregateException (seq { yield! left; yield! Seq.except left (f y) }) :> exn
4446
#endif
4547

4648
static member inline Invoke (x: 'Plus) (y: 'Plus) : 'Plus =
@@ -116,13 +118,13 @@ type Plus with
116118
#if !FABLE_COMPILER
117119
type Plus with
118120

119-
static member inline ``+`` (x: 'a Task, y: 'a Task, [<Optional>]_mthd: Plus) = Task.map2 Plus.Invoke x y
121+
static member inline ``+`` (x: 'a Task, y: 'a Task, [<Optional>]_mthd: Plus) = Task.lift2 Plus.Invoke x y
120122
#endif
121123

122124
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
123125
type Plus with
124126

125-
static member inline ``+`` (x: 'a ValueTask, y: 'a ValueTask, [<Optional>]_mthd: Plus) = ValueTask.map2 Plus.Invoke x y
127+
static member inline ``+`` (x: 'a ValueTask, y: 'a ValueTask, [<Optional>]_mthd: Plus) = ValueTask.lift2 Plus.Invoke x y
126128

127129
#endif
128130

@@ -138,7 +140,7 @@ type Plus with
138140

139141
static member inline ``+`` (f: 'T->'Monoid, g: 'T->'Monoid, [<Optional>]_mthd: Plus) = (fun x -> Plus.Invoke (f x) (g x)) : 'T->'Monoid
140142

141-
static member inline ``+`` (x: 'S Async , y: 'S Async , [<Optional>]_mthd: Plus) = Async.map2 Plus.Invoke x y
143+
static member inline ``+`` (x: 'S Async , y: 'S Async , [<Optional>]_mthd: Plus) = Async.lift2 Plus.Invoke x y
142144

143145
static member inline ``+`` (x: 'a Expr , y: 'a Expr , [<Optional>]_mthd: Plus) : 'a Expr =
144146
let inline f (x: 'a) : 'a -> 'a = Plus.Invoke x

0 commit comments

Comments
 (0)