Skip to content

Commit

Permalink
Add missing map3/lift3 extensions (#403)
Browse files Browse the repository at this point in the history

Co-authored-by: pawel.zelmanski <[email protected]>
  • Loading branch information
2 people authored and wallymathieu committed Jan 6, 2021
1 parent cf8126e commit 15b2434
Show file tree
Hide file tree
Showing 16 changed files with 353 additions and 19 deletions.
39 changes: 38 additions & 1 deletion src/FSharpPlus/Control/Applicative.fs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,45 @@ type Lift2 with

static member inline Lift2 (_, (_:'t when 't: null and 't: struct, _: ^u when ^u : null and ^u: struct), _mthd: Default1) = id
static member inline Lift2 (f: 'T -> 'U -> 'V, (x: '``Applicative<'T>``, y: '``Applicative<'U>``) , _mthd: Default1) = ((^``Applicative<'T>`` or ^``Applicative<'U>`` ) : (static member Lift2 : _*_*_ -> _) f, x, y)


type Lift3 =
inherit Default1

static member Lift3 (f, (x: Lazy<_> , y: Lazy<_> , z: Lazy<_> ), _mthd: Lift3) = Lazy.map3 f x y z
static member Lift3 (f, (x: seq<_> , y: seq<_> , z: seq<_> ), _mthd: Lift3) = Seq.lift3 f x y z
static member Lift3 (f, (x: NonEmptySeq<_> , y: NonEmptySeq<_> , z: NonEmptySeq<_> ), _mthd: Lift3) = NonEmptySeq.lift3 f x y z
#if !FABLE_COMPILER
static member Lift3 (f, (x: IEnumerator<_> , y: IEnumerator<_> , z: IEnumerator<_> ), _mthd: Lift3) = Enumerator.map3 f x y z
#endif
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = List.lift3 f x y z
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Array.lift3 f x y z
static member Lift3 (f, (x: 'R -> 'T , y: 'R -> 'U , z: 'R -> 'V ), _mthd: Lift3) = fun a -> f (x a) (y a) (z a)
#if !FABLE_COMPILER
static member Lift3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Lift3) = Task.map3 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) = Option.map3 f x y z
static member Lift3 (f, (x: Result<'T,'Error> , y: Result<'U,'Error> , z: Result<'V, 'Error> ), _mthd: Lift3) = Result.map3 f x y z
static member Lift3 (f, (x: Choice<'T,'Error> , y: Choice<'U,'Error> , z: Choice<'V, 'Error> ), _mthd: Lift3) = Choice.map3 f x y z
static member Lift3 (f, (x: Map<'Key,'T> , y: Map<'Key,'U> , z: Map<'Key, 'V> ), _mthd: Lift3) = Map.mapValues3 f x y z
static member Lift3 (f, (x: Dictionary<'Key,'T>, y: Dictionary<'Key,'U>, z: Dictionary<'Key, 'V>), _mthd: Lift3) = Dictionary.map3 f x y z
#if !FABLE_COMPILER
static member Lift3 (f, (x: Expr<'T> , y: Expr<'U> , z: Expr<'V> ), _mthd: Lift3) = <@ f %x %y %z @>
#endif
static member Lift3 (f, (x: ResizeArray<'T> , y: ResizeArray<'U> , z: ResizeArray<'V> ), _mthd: Lift3) = ResizeArray.lift3 f x y z

static member inline Invoke (f: 'T -> 'U -> 'V -> 'W) (x: '``Applicative<'T>``) (y: '``Applicative<'U>``) (z: '``Applicative<'V>``): '``Applicative<'W>`` =
let inline call (mthd : ^M, input1: ^I1, input2: ^I2, input3: ^I3, _output: ^R) =
((^M or ^I1 or ^I2 or ^I3 or ^R) : (static member Lift3 : _*(_*_*_)*_ -> _) f, (input1, input2, input3), mthd)
call (Unchecked.defaultof<Lift3>, x, y, z, Unchecked.defaultof<'``Applicative<'W>``>)

static member inline InvokeOnInstance (f: 'T -> 'U -> 'V -> 'W) (x: '``Applicative<'T>``) (y: '``Applicative<'U>``) (z: '``Applicative<'V>``)=
((^``Applicative<'T>`` or ^``Applicative<'U>`` or ^``Applicative<'V>``) : (static member Lift3 : _*_*_*_ -> _) f, x, y, z)

type Lift3 with
static member inline Lift3 (f, (x, y, z), _mthd: Default3) = ((((Return.InvokeOnInstance f, x) ||> Apply.InvokeOnInstance), y) ||> Apply.InvokeOnInstance, z) ||> Apply.InvokeOnInstance
static member inline Lift3 (_, (_:'t when 't: null and 't: struct, _: ^u when ^u : null and ^u: struct, _: ^v when ^v : null and ^v: struct), _mthd: Default1) = id
static member inline Lift3 (f: 'T -> 'U -> 'V -> 'W, (x: '``Applicative<'T>``, y: '``Applicative<'U>``, z: '``Applicative<'V>``) , _mthd: Default1) = ((^``Applicative<'T>`` or ^``Applicative<'U>`` or ^``Applicative<'V>`` ) : (static member Lift3 : _*_*_*_ -> _) f, x, y, z)

type IsLeftZero =
inherit Default1
Expand Down
13 changes: 13 additions & 0 deletions src/FSharpPlus/Data/NonEmptySeq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,19 @@ module NonEmptySeq =
let apply f x = bind (fun f -> map ((<|) f) x) f

let lift2 f x1 x2 = allPairs x1 x2 |> map (fun (x, y) -> f x y)

/// <summary>Combines values from three NonEmptySeq and calls a mapping function on this combination.</summary>
/// <param name="f">Mapping function taking three element combination as input.</param>
/// <param name="x1">First NonEmptySeq.</param>
/// <param name="x2">Second NonEmptySeq.</param>
/// <param name="x3">Third NonEmptySeq.</param>
///
/// <returns>NonEmptySeq with values returned from mapping function.</returns>
let lift3 f x1 x2 x3 =
allPairs x2 x3
|> allPairs x1
|> map (fun x -> (fst (snd x), snd (snd x), fst x))
|> map (fun (x, y, z) -> f x y z)

let replace (oldValue: NonEmptySeq<'T>) (newValue: NonEmptySeq<'T>) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> =
Seq.replace oldValue newValue source |> unsafeOfSeq
Expand Down
15 changes: 15 additions & 0 deletions src/FSharpPlus/Extensions/Array.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ module Array =
let lift2 f x y =
let lenx, leny = Array.length x, Array.length y
Array.init (lenx * leny) (fun i -> f x.[i / leny] y.[i % leny])


/// <summary>Combines all values from three arrays and calls a mapping function on this combination.</summary>
/// <param name="f">Mapping function taking three element combination as input.</param>
/// <param name="x1">First array.</param>
/// <param name="x2">Second array.</param>
/// <param name="x3">Third array.</param>
///
/// <returns>Array with values returned from mapping function.</returns>
let lift3 f x y z =
let lenx, leny, lenz = Array.length x, Array.length y, Array.length z
let combinedFirstTwo = Array.init (lenx * leny) (fun i -> (x.[i / leny], y.[i % leny]))

Array.init (lenx * leny * lenz) (fun i -> combinedFirstTwo.[i/leny], z.[i%leny])
|> Array.map (fun x -> f (fst (fst x)) (snd (fst x)) (snd x))

/// Concatenates all elements, using the specified separator between each element.
let intercalate (separator: _ []) (source: seq<_ []>) = source |> Seq.intercalate separator |> Seq.toArray
Expand Down
12 changes: 12 additions & 0 deletions src/FSharpPlus/Extensions/Async.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ module Async =
let! a = x
let! b = y
return f a b}

/// <summary>Creates an async workflow from three workflows 'x', 'y' and 'z', mapping its results with 'f'.</summary>
/// <remarks>Workflows are run in sequence.</remarks>
/// <param name="f">The mapping function.</param>
/// <param name="x">First async workflow.</param>
/// <param name="y">Second async workflow.</param>
/// <param name="z">third async workflow.</param>
let map3 f x y z = async {
let! a = x
let! b = y
let! c = z
return f a b c}

/// <summary>Creates an async workflow from two workflows 'x' and 'y', tupling its results.</summary>
let zip x y = async {
Expand Down
13 changes: 13 additions & 0 deletions src/FSharpPlus/Extensions/Choice.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ module Choice =
/// <returns>The combined value, or the first Choice2Of2.</returns>
let map2 f (x: Choice<'T,'Error>) (y: Choice<'U,'Error>) : Choice<'V,'Error> = match x, y with Choice1Of2 a, Choice1Of2 b -> Choice1Of2 (f a b) | Choice2Of2 e, _ | _, Choice2Of2 e -> Choice2Of2 e

/// <summary>Creates a Choice value from three of Choice values, using a function to combine the Choice1Of2 values.</summary>
/// <param name="x">The first Choice value.</param>
/// <param name="y">The second Choice value.</param>
/// <param name="z">The third Choice value.</param>
///
/// <returns>The combined value, or the first Choice2Of2.</returns>
let map3 f (x: Choice<'T,'Error>) (y: Choice<'U,'Error>) (z: Choice<'V, 'Error>) : Choice<'W,'Error> =
match x, y, z with
| Choice1Of2 a, Choice1Of2 b, Choice1Of2 c -> Choice1Of2 (f a b c)
| Choice2Of2 e, _ , _
| _ , Choice2Of2 e, _
| _ , _ , Choice2Of2 e -> Choice2Of2 e

/// <summary>Flattens two nested Choice.</summary>
/// <param name="source">The nested Choice.</param>
/// <returns>A single Choice1Of2 of the value when it was nested with Choice1Of2s, or the Choice2Of2.</returns>
Expand Down
17 changes: 17 additions & 0 deletions src/FSharpPlus/Extensions/Dictionary.fs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ module Dictionary =
| Some vy -> dct.Add (k, f.Invoke (vx, vy))
| None -> ()
dct

/// <summary>Combines values from three Dictionaries using mapping function.</summary>
/// <remarks>Keys that are not present on every Dictionary are dropped.</remarks>
/// <param name="f">The mapping function.</param>
/// <param name="x">First input Dictionary.</param>
/// <param name="y">Second input Dictionary.</param>
/// <param name="y">Third input Dictionary.</param>
///
/// <returns>The mapped Dictionary.</returns>
let map3 f (x: Dictionary<'Key, 'T1>) (y: Dictionary<'Key, 'T2>) (z: Dictionary<'Key, 'T3>) =
let dct = Dictionary<'Key, 'U> ()
let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt f
for KeyValue(k, vx) in x do
match tryGetValue k y, tryGetValue k z with
| Some vy, Some vz -> dct.Add (k, f.Invoke (vx, vy, vz))
| _ , _ -> ()
dct

/// <summary>Applies given function to each value of the given dictionary.</summary>
/// <param name="f">The mapping function.</param>
Expand Down
8 changes: 8 additions & 0 deletions src/FSharpPlus/Extensions/Lazy.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ module Lazy =

/// <summary>Creates a Lazy value from a pair of Lazy values, using a mapping function to combine them.</summary>
let map2 (mapping: 'T->'U->'V) (x: Lazy<'T>) (y: Lazy<'U>) = Lazy<_>.Create (fun () -> mapping x.Value y.Value)

/// <summary>Creates a Lazy value from three Lazy values, using a function to combine them.</summary>
/// <param name="x">The first Lazy value.</param>
/// <param name="y">The second Lazy value.</param>
/// <param name="z">The third Lazy value.</param>
///
/// <returns>The combined value.</returns>
let map3 (f: 'T->'U->'V->'W) (x: Lazy<'T>) (y: Lazy<'U>) (z: Lazy<'V>) = Lazy<_>.Create (fun () -> f x.Value y.Value z.Value)

/// <summary>Applies a Lazy value to a Lazy function.</summary>
/// <param name="f">The Lazy function.</param>
Expand Down
13 changes: 13 additions & 0 deletions src/FSharpPlus/Extensions/List.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ module List =

/// Combines all values from the first list with the second, using the supplied mapping function.
let lift2 f x1 x2 = List.allPairs x1 x2 |> List.map (fun (x, y) -> f x y)

/// <summary>Combines values from three list and calls a mapping function on this combination.</summary>
/// <param name="f">Mapping function taking three element combination as input.</param>
/// <param name="x1">First list.</param>
/// <param name="x2">Second list.</param>
/// <param name="x3">Third list.</param>
///
/// <returns>List with values returned from mapping function.</returns>
let lift3 f x1 x2 x3 =
List.allPairs x2 x3
|> List.allPairs x1
|> List.map (fun x -> (fst (snd x), snd (snd x), fst x))
|> List.map (fun (x, y, z) -> f x y z)

/// Returns a list with all possible tails of the source list.
let tails x = let rec loop = function [] -> [] | _::xs as s -> s::(loop xs) in loop x
Expand Down
15 changes: 15 additions & 0 deletions src/FSharpPlus/Extensions/Map.fs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ module Map =
| Some vy -> yield (k, f.Invoke (vx, vy))
| None -> () }

/// <summary>Combines values from three maps using mapping function.</summary>
/// <remarks>Keys that are not present on every Map are dropped.</remarks>
/// <param name="f">The mapping function.</param>
/// <param name="x">First input Map.</param>
/// <param name="y">Second input Map.</param>
/// <param name="y">Third input Map.</param>
///
/// <returns>The mapped Map.</returns>
let mapValues3 f (x: Map<'Key, 'T1>) (y: Map<'Key, 'T2>) (z: Map<'Key, 'T3>) = Map <| seq {
let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt f
for KeyValue(k, vx) in x do
match Map.tryFind k y, lazy Map.tryFind k z with
| Some vy, Lazy (Some vz) -> yield (k, f.Invoke (vx, vy, vz))
| _ , _ -> () }

/// <summary>Applies given function to each value of the given Map.</summary>
/// <param name="f">The mapping function.</param>
/// <param name="x">The input Map.</param>
Expand Down
10 changes: 10 additions & 0 deletions src/FSharpPlus/Extensions/ResizeArray.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ module ResizeArray =
/// Combines all values from the first ResizeArray with the second, using the supplied mapping function.
let lift2 mapping (x1: ResizeArray<'T>) (x2: ResizeArray<'U>) = ResizeArray (Seq.lift2 mapping x1 x2)

/// <summary>Combines values from three ResizeArrays and calls a mapping function on this combination.</summary>
/// <param name="f">Mapping function taking three element combination as input.</param>
/// <param name="x1">First ResizeArray.</param>
/// <param name="x2">Second ResizeArray.</param>
/// <param name="x3">Third ResizeArray.</param>
///
/// <returns>ResizeArray with values returned from mapping function.</returns>
let lift3 mapping (x1: ResizeArray<'T>) (x2: ResizeArray<'U>) (x3: ResizeArray<'V>) =
ResizeArray (Seq.lift3 mapping x1 x2 x3)

/// Concatenates all elements, using the specified separator between each element.
let intercalate (separator: _ []) (source: seq<_ []>) = source |> Seq.intercalate separator |> Seq.toArray

Expand Down
13 changes: 13 additions & 0 deletions src/FSharpPlus/Extensions/Result.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ module Result =
/// <returns>The combined value, or the first Error.</returns>
let map2 f (x: Result<'T,'Error>) (y: Result<'U,'Error>) : Result<'V,'Error> = match x, y with Ok a, Ok b -> Ok (f a b) | Error e, _ | _, Error e -> Error e

/// <summary>Creates a Result value from three Result values, using a function to combine them.</summary>
/// <param name="x">The first Result value.</param>
/// <param name="y">The second Result value.</param>
/// <param name="z">The third Result value.</param>
///
/// <returns>The combined value, or the first Error.</returns>
let map3 f (x: Result<'T, 'Error>) (y: Result<'U, 'Error>) (z: Result<'V, 'Error>): Result<'V, 'Error> =
match x, y, z with
| Ok a, Ok b, Ok c -> Ok(f a b c)
| Error e, _, _
| _, Error e, _
| _, _, Error e -> Error e

/// <summary>Flattens two nested Results.</summary>
/// <param name="source">The nested Results.</param>
/// <returns>A single Ok of the value when it was nested with OKs, or the Error.</returns>
Expand Down
14 changes: 14 additions & 0 deletions src/FSharpPlus/Extensions/Seq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ module Seq =

/// Combines all values from the first seq with the second, using the supplied mapping function.
let lift2 f x1 x2 = Seq.allPairs x1 x2 |> Seq.map (fun (x, y) -> f x y)


/// <summary>Combines values from three seq and calls a mapping function on this combination.</summary>
/// <param name="f">Mapping function taking three element combination as input.</param>
/// <param name="x1">First seq.</param>
/// <param name="x2">Second seq.</param>
/// <param name="x3">Third seq.</param>
///
/// <returns>Seq with values returned from mapping function.</returns>
let lift3 f x1 x2 x3 =
Seq.allPairs x2 x3
|> Seq.allPairs x1
|> Seq.map (fun x -> (fst (snd x), snd (snd x), fst x))
|> Seq.map (fun (x, y, z) -> f x y z)

/// <summary>
/// Applies a function to each element of the collection, starting from the end,
Expand Down
Loading

0 comments on commit 15b2434

Please sign in to comment.