Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non sequential Applicatives #559

Merged
merged 29 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
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
138 changes: 138 additions & 0 deletions docsrc/content/abstraction-zipapplicative.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r @"../../src/FSharpPlus/bin/Release/netstandard2.0/FSharpPlus.dll"

(**
ZipApplicative
==============
A functor with application, providing operations to embed pure expressions (``pur``), run computations pointwise and/or paralell and combine their results (``<.>``).
___
Minimal complete definition
---------------------------
* ``pur x`` &nbsp; . &nbsp; ``result x``
* ``(<.>) f x``
*)
(**
static member Pure (x: 'T) : 'ZipApplicative<'T>
static member (<.>) (f: 'ZipApplicative<'T -> 'U>, x: 'ZipApplicative<'T>) : 'ZipApplicative<'U>
*)
(**


Other operations
----------------

* ``zip``
*)
(**
static member Zip (x1: 'ZipApplicative<'T1>, x2: 'ZipApplicative<'T2>) : 'ZipApplicative<'T1 * 'T2>
*)
(**
* ``map2``
*)
(**
static member Map2 (f: 'T1 -> 'T2 -> 'T, x1: 'ZipApplicative<'T1>, x2: 'ZipApplicative<'T2>) : 'ZipApplicative<'T>
*)

(**
* ``map3``
*)
(**
static member Map3 (f: 'T1 -> 'T2 -> 'T3 -> 'T, x1: 'ZipApplicative<'T1>, x2: 'ZipApplicative<'T2>, x3: 'ZipApplicative<'T3>) : 'ZipApplicative<'T>
*)

(**


Rules
-----
*)
(**
pur id <.> v = v
pur (<<) <.> u <.> v <.> w = u <.> (v <.> w)
pur f <*> pur x = pur (f x)
u <*> pur y = pur ((|>) y) <.> u
*)
(**
Related Abstractions
--------------------
- [Functor](abstraction-functor.html): A zipApplicative is a functor whose ``map`` operation can be splitted in ``pur`` and ``(<.>)`` operations,

- [ZipApplicative](abstraction-applicative.html) : ZipApplicatives are applicatives which usually don't form a [Monad](abstraction-monad.html).

Concrete implementations
------------------------
From F#

- ``seq<'T>``
- ``list<'T>``
- ``option<'T>`` *
- ``voption<'T>`` *
- ``Lazy<'T>`` *
- ``Async<'T>``
- ``Result<'T, 'U>``
- ``Choice<'T, 'U>``
- ``KeyValuePair<'Key, 'T>`` *
- ``'Monoid * 'T`` *
- ``ValueTuple<'Monoid, 'T>`` *
- ``Task<'T>``
- ``ValueTask<'T>``
- ``'R -> 'T`` *
- ``Expr<'T>`` *


From F#+

- [``NonEmptySeq<'T>``]
- [``NonEmptyList<'T>``](type-nonempty.html)
- [``Compose<'ZipApplicative1<'ZipApplicative2<'T>>>``](type-compose.html)

(*) The operation is the same as that for the normal applicative


Only for <*> operation:
- ``array<'T>``
- ``ResizeArray<'T>``
- ``Map<'Key, 'T>``
- ``Dictionary<'Key, 'T>``
- ``IDictionary<'Key, 'T>``
- ``IReadOnlyDictionary<'Key, 'T>``


[Suggest another](https://github.com/fsprojects/FSharpPlus/issues/new) concrete implementation

Examples
--------
*)


(**
```f#
#r @"nuget: FSharpPlus"
```
*)

open FSharpPlus


// pointwise operations

let arr1 = (+) <!> [|1;2;3|] <*> [|10;20;30|]
let arr2 = (+) <!> [|1;2;3|] <.> [|10;20;30|]

// val arr1: int array = [|11; 21; 31; 12; 22; 32; 13; 23; 33|]
// val arr2: int array = [|11; 22; 33|]


// Validations

let validated = app2 {
let! x = async { return Ok 1 }
and! y = async { return Ok 2 }
and! z = async { return Error ["Error"] }
return x + y + z
}

validated |> Async.RunSynchronously
// val it: Result<int,string list> = Error ["Error"]
31 changes: 28 additions & 3 deletions src/FSharpPlus/Builders.fs
Original file line number Diff line number Diff line change
Expand Up @@ -210,20 +210,45 @@ module GenericBuilders =
member _.Run x : '``Applicative1<Applicative2<Applicative3<'T>>>`` = x


/// Generic ZipApplicative CE builder.
type ZipApplicativeBuilder<'``applicative<'t>``> () =
member _.ReturnFrom (expr) = expr : '``applicative<'t>``
member inline _.Return (x: 'T) = pur x : '``Applicative<'T>``
member inline _.Yield (x: 'T) = pur x : '``Applicative<'T>``
member inline _.BindReturn(x, [<InlineIfLambda>]f) = map f x : '``Applicative<'U>``
member inline _.MergeSources (t1: '``Applicative<'T>``, t2: '``Applicative<'U>``) : '``Applicative<'T * 'U>`` = map2 tuple2 t1 t2
member inline _.MergeSources3 (t1: '``Applicative<'T>``, t2: '``Applicative<'U>``, t3: '``Applicative<'V>``) : '``Applicative<'T * 'U * 'V>`` = map3 tuple3 t1 t2 t3
member _.Run f : '``Applicative<'T>`` = f

/// Generic 2 layers ZipApplicative CE builder.
type ZipApplicativeBuilder2<'``applicative1<applicative2<'t>>``> () =
member _.ReturnFrom expr : '``applicative1<applicative2<'t>>`` = expr
member inline _.Return (x: 'T) : '``Applicative1<Applicative2<'T>>`` = (pur >> pur) x
member inline _.Yield (x: 'T) : '``Applicative1<Applicative2<'T>>`` = (pur >> pur) x
member inline _.BindReturn (x: '``Applicative1<Applicative2<'T>>``, [<InlineIfLambda>]f: _ -> _) : '``Applicative1<Applicative2<'U>>`` = (map >> map) f x
member inline _.MergeSources (t1, t2) : '``Applicative1<Applicative2<'T>>`` = (map2 >> map2) tuple2 t1 t2
member inline _.MergeSources3 (t1, t2, t3) : '``Applicative1<Applicative2<'T>>`` = (map3 >> map3) tuple3 t1 t2 t3
member _.Run x : '``Applicative1<Applicative2<'T>>`` = x

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

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

/// Creates an applicative computation expression.
/// Creates a (sequential) applicative computation expression.
let applicative<'``Applicative<'T>``> = ApplicativeBuilder<'``Applicative<'T>``> ()

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

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

/// Creates a (non sequential) applicative computation expression.
let app<'``ZipApplicative<'T>``> = ZipApplicativeBuilder<'``ZipApplicative<'T>``> ()

/// Creates a (non sequential) applicative computation expression which compose effects of two Applicatives.
let app2<'``ZipApplicative1<ZipApplicative2<'T>>``> = ZipApplicativeBuilder2<'``ZipApplicative1<ZipApplicative2<'T>>``> ()

#endif
12 changes: 6 additions & 6 deletions src/FSharpPlus/Control/Applicative.fs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ type Lift2 =
static member inline Lift2 (f, ((a: 'Monoid, x: 'T) , (b: 'Monoid, y: 'U) ), _mthd: Lift2) = Plus.Invoke a b, f x y
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)
#if !FABLE_COMPILER
static member Lift2 (f, (x: Task<'T> , y: Task<'U> ), _mthd: Lift2) = Task.map2 f x y
static member Lift2 (f, (x: Task<'T> , y: Task<'U> ), _mthd: Lift2) = Task.lift2 f x y
#endif
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
static member Lift2 (f, (x: ValueTask<'T> , y: ValueTask<'U> ), _mthd: Lift2) = ValueTask.map2 f x y
static member Lift2 (f, (x: ValueTask<'T> , y: ValueTask<'U> ), _mthd: Lift2) = ValueTask.lift2 f x y
#endif
static member Lift2 (f, (x , y ), _mthd: Lift2) = Async.map2 f x y
static member Lift2 (f, (x , y ), _mthd: Lift2) = Async.lift2 f x y
static member Lift2 (f, (x , y ), _mthd: Lift2) = Option.map2 f x y

#if !FABLE_COMPILER
Expand Down Expand Up @@ -158,12 +158,12 @@ type Lift3 =
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
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)
#if !FABLE_COMPILER
static member Lift3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Lift3) = Task.map3 f x y z
static member Lift3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Lift3) = Task.lift3 f x y z
#endif
#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
static member Lift3 (f, (x: ValueTask<'T> , y: ValueTask<'U> , z: ValueTask<'V> ), _mthd: Lift3) = ValueTask.map3 f x y z
static member Lift3 (f, (x: ValueTask<'T> , y: ValueTask<'U> , z: ValueTask<'V> ), _mthd: Lift3) = ValueTask.lift3 f x y z
#endif
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Async.map3 f x y z
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Async.lift3 f x y z
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Option.map3 f x y z

#if !FABLE_COMPILER
Expand Down
5 changes: 3 additions & 2 deletions src/FSharpPlus/Control/Functor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,9 @@ type Zip =
static member Zip ((x: 'T [] , y: 'U [] , _output: ('T*'U) [] ), _mthd: Zip) = Array.zipShortest x y
static member Zip ((x: ResizeArray<'T> , y: ResizeArray<'U> , _output: ResizeArray<'T*'U> ), _mthd: Zip) = ResizeArray.zipShortest x y
static member Zip ((x: option<'T> , y: option<'U> , _output: option<'T*'U> ), _mthd: Zip) = Option.zip x y
static member Zip ((x: voption<'T> , y: voption<'U> , _output: voption<'T*'U> ), _mthd: Zip) = ValueOption.zip x y
static member Zip ((x: Result<'T, 'Error> , y: Result<'U, 'Error> , _output: Result<'T * 'U, 'Error> ), _mthd: Zip) = Result.zip x y
static member Zip ((x: voption<'T> , y: voption<'U> , _output: voption<'T*'U> ), _mthd: Zip) = ValueOption.zip x y
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
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
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
Expand Down
2 changes: 2 additions & 0 deletions src/FSharpPlus/Control/MonadOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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 pur x = Pure.Invoke x
let inline (<.>) f x = ZipApply.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

Expand Down
12 changes: 7 additions & 5 deletions src/FSharpPlus/Control/Monoid.fs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@
static member ``+`` (x: AggregateException, y: AggregateException, [<Optional>]_mthd: Plus ) = new AggregateException (seq {yield! x.InnerExceptions; yield! y.InnerExceptions})
static member ``+`` (x: exn , y: exn , [<Optional>]_mthd: Plus ) =
let f (e: exn) = match e with :? AggregateException as a -> a.InnerExceptions :> seq<_> | _ -> Seq.singleton e
new AggregateException (seq {yield! f x; yield! f y}) :> exn
let left = f x
new AggregateException (seq { yield! left; yield! Seq.except left (f y) }) :> exn
#else
static member ``+`` (x: StringBuilder , y: StringBuilder , [<Optional>]_mthd: Plus ) = StringBuilder().Append(string x).Append(string y)
static member ``+`` (_: Id0 , _: Id0 , [<Optional>]_mthd: Plus ) = Id0 ""
static member ``+`` (x: exn , y: exn , [<Optional>]_mthd: Plus ) : exn =
let f (e: exn) = match e with :? AggregateException as a -> a.Data0 :> seq<_> | _ -> Seq.singleton e
AggregateException (seq {yield! f x; yield! f y})
let left = f x
AggregateException (seq { yield! left; yield! Seq.except left (f y) }) :> exn

Check warning on line 45 in src/FSharpPlus/Control/Monoid.fs

View workflow job for this annotation

GitHub Actions / testFable3SubsetOnCore

This upcast is unnecessary - the types are identical
#endif

static member inline Invoke (x: 'Plus) (y: 'Plus) : 'Plus =
Expand Down Expand Up @@ -116,13 +118,13 @@
#if !FABLE_COMPILER
type Plus with

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

#if !NET45 && !NETSTANDARD2_0 && !FABLE_COMPILER
type Plus with

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

#endif

Expand All @@ -138,7 +140,7 @@

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

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

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