Skip to content

Commit 15b2434

Browse files
pzelmanskipawel.zelmanski
authored andcommitted
Add missing map3/lift3 extensions (#403)
Co-authored-by: pawel.zelmanski <pawel.zelmanski@ihsmarkit.com>
1 parent cf8126e commit 15b2434

File tree

16 files changed

+353
-19
lines changed

16 files changed

+353
-19
lines changed

src/FSharpPlus/Control/Applicative.fs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,45 @@ type Lift2 with
109109

110110
static member inline Lift2 (_, (_:'t when 't: null and 't: struct, _: ^u when ^u : null and ^u: struct), _mthd: Default1) = id
111111
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)
112-
113112

113+
type Lift3 =
114+
inherit Default1
115+
116+
static member Lift3 (f, (x: Lazy<_> , y: Lazy<_> , z: Lazy<_> ), _mthd: Lift3) = Lazy.map3 f x y z
117+
static member Lift3 (f, (x: seq<_> , y: seq<_> , z: seq<_> ), _mthd: Lift3) = Seq.lift3 f x y z
118+
static member Lift3 (f, (x: NonEmptySeq<_> , y: NonEmptySeq<_> , z: NonEmptySeq<_> ), _mthd: Lift3) = NonEmptySeq.lift3 f x y z
119+
#if !FABLE_COMPILER
120+
static member Lift3 (f, (x: IEnumerator<_> , y: IEnumerator<_> , z: IEnumerator<_> ), _mthd: Lift3) = Enumerator.map3 f x y z
121+
#endif
122+
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = List.lift3 f x y z
123+
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Array.lift3 f x y z
124+
static member Lift3 (f, (x: 'R -> 'T , y: 'R -> 'U , z: 'R -> 'V ), _mthd: Lift3) = fun a -> f (x a) (y a) (z a)
125+
#if !FABLE_COMPILER
126+
static member Lift3 (f, (x: Task<'T> , y: Task<'U> , z: Task<'V> ), _mthd: Lift3) = Task.map3 f x y z
127+
#endif
128+
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Async.map3 f x y z
129+
static member Lift3 (f, (x , y , z ), _mthd: Lift3) = Option.map3 f x y z
130+
static member Lift3 (f, (x: Result<'T,'Error> , y: Result<'U,'Error> , z: Result<'V, 'Error> ), _mthd: Lift3) = Result.map3 f x y z
131+
static member Lift3 (f, (x: Choice<'T,'Error> , y: Choice<'U,'Error> , z: Choice<'V, 'Error> ), _mthd: Lift3) = Choice.map3 f x y z
132+
static member Lift3 (f, (x: Map<'Key,'T> , y: Map<'Key,'U> , z: Map<'Key, 'V> ), _mthd: Lift3) = Map.mapValues3 f x y z
133+
static member Lift3 (f, (x: Dictionary<'Key,'T>, y: Dictionary<'Key,'U>, z: Dictionary<'Key, 'V>), _mthd: Lift3) = Dictionary.map3 f x y z
134+
#if !FABLE_COMPILER
135+
static member Lift3 (f, (x: Expr<'T> , y: Expr<'U> , z: Expr<'V> ), _mthd: Lift3) = <@ f %x %y %z @>
136+
#endif
137+
static member Lift3 (f, (x: ResizeArray<'T> , y: ResizeArray<'U> , z: ResizeArray<'V> ), _mthd: Lift3) = ResizeArray.lift3 f x y z
138+
139+
static member inline Invoke (f: 'T -> 'U -> 'V -> 'W) (x: '``Applicative<'T>``) (y: '``Applicative<'U>``) (z: '``Applicative<'V>``): '``Applicative<'W>`` =
140+
let inline call (mthd : ^M, input1: ^I1, input2: ^I2, input3: ^I3, _output: ^R) =
141+
((^M or ^I1 or ^I2 or ^I3 or ^R) : (static member Lift3 : _*(_*_*_)*_ -> _) f, (input1, input2, input3), mthd)
142+
call (Unchecked.defaultof<Lift3>, x, y, z, Unchecked.defaultof<'``Applicative<'W>``>)
143+
144+
static member inline InvokeOnInstance (f: 'T -> 'U -> 'V -> 'W) (x: '``Applicative<'T>``) (y: '``Applicative<'U>``) (z: '``Applicative<'V>``)=
145+
((^``Applicative<'T>`` or ^``Applicative<'U>`` or ^``Applicative<'V>``) : (static member Lift3 : _*_*_*_ -> _) f, x, y, z)
146+
147+
type Lift3 with
148+
static member inline Lift3 (f, (x, y, z), _mthd: Default3) = ((((Return.InvokeOnInstance f, x) ||> Apply.InvokeOnInstance), y) ||> Apply.InvokeOnInstance, z) ||> Apply.InvokeOnInstance
149+
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
150+
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)
114151

115152
type IsLeftZero =
116153
inherit Default1

src/FSharpPlus/Data/NonEmptySeq.fs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,19 @@ module NonEmptySeq =
501501
let apply f x = bind (fun f -> map ((<|) f) x) f
502502

503503
let lift2 f x1 x2 = allPairs x1 x2 |> map (fun (x, y) -> f x y)
504+
505+
/// <summary>Combines values from three NonEmptySeq and calls a mapping function on this combination.</summary>
506+
/// <param name="f">Mapping function taking three element combination as input.</param>
507+
/// <param name="x1">First NonEmptySeq.</param>
508+
/// <param name="x2">Second NonEmptySeq.</param>
509+
/// <param name="x3">Third NonEmptySeq.</param>
510+
///
511+
/// <returns>NonEmptySeq with values returned from mapping function.</returns>
512+
let lift3 f x1 x2 x3 =
513+
allPairs x2 x3
514+
|> allPairs x1
515+
|> map (fun x -> (fst (snd x), snd (snd x), fst x))
516+
|> map (fun (x, y, z) -> f x y z)
504517

505518
let replace (oldValue: NonEmptySeq<'T>) (newValue: NonEmptySeq<'T>) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> =
506519
Seq.replace oldValue newValue source |> unsafeOfSeq

src/FSharpPlus/Extensions/Array.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ module Array =
2525
let lift2 f x y =
2626
let lenx, leny = Array.length x, Array.length y
2727
Array.init (lenx * leny) (fun i -> f x.[i / leny] y.[i % leny])
28+
29+
30+
/// <summary>Combines all values from three arrays and calls a mapping function on this combination.</summary>
31+
/// <param name="f">Mapping function taking three element combination as input.</param>
32+
/// <param name="x1">First array.</param>
33+
/// <param name="x2">Second array.</param>
34+
/// <param name="x3">Third array.</param>
35+
///
36+
/// <returns>Array with values returned from mapping function.</returns>
37+
let lift3 f x y z =
38+
let lenx, leny, lenz = Array.length x, Array.length y, Array.length z
39+
let combinedFirstTwo = Array.init (lenx * leny) (fun i -> (x.[i / leny], y.[i % leny]))
40+
41+
Array.init (lenx * leny * lenz) (fun i -> combinedFirstTwo.[i/leny], z.[i%leny])
42+
|> Array.map (fun x -> f (fst (fst x)) (snd (fst x)) (snd x))
2843

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

src/FSharpPlus/Extensions/Async.fs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ module Async =
1818
let! a = x
1919
let! b = y
2020
return f a b}
21+
22+
/// <summary>Creates an async workflow from three workflows 'x', 'y' and 'z', mapping its results with 'f'.</summary>
23+
/// <remarks>Workflows are run in sequence.</remarks>
24+
/// <param name="f">The mapping function.</param>
25+
/// <param name="x">First async workflow.</param>
26+
/// <param name="y">Second async workflow.</param>
27+
/// <param name="z">third async workflow.</param>
28+
let map3 f x y z = async {
29+
let! a = x
30+
let! b = y
31+
let! c = z
32+
return f a b c}
2133

2234
/// <summary>Creates an async workflow from two workflows 'x' and 'y', tupling its results.</summary>
2335
let zip x y = async {

src/FSharpPlus/Extensions/Choice.fs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ module Choice =
3030
/// <returns>The combined value, or the first Choice2Of2.</returns>
3131
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
3232

33+
/// <summary>Creates a Choice value from three of Choice values, using a function to combine the Choice1Of2 values.</summary>
34+
/// <param name="x">The first Choice value.</param>
35+
/// <param name="y">The second Choice value.</param>
36+
/// <param name="z">The third Choice value.</param>
37+
///
38+
/// <returns>The combined value, or the first Choice2Of2.</returns>
39+
let map3 f (x: Choice<'T,'Error>) (y: Choice<'U,'Error>) (z: Choice<'V, 'Error>) : Choice<'W,'Error> =
40+
match x, y, z with
41+
| Choice1Of2 a, Choice1Of2 b, Choice1Of2 c -> Choice1Of2 (f a b c)
42+
| Choice2Of2 e, _ , _
43+
| _ , Choice2Of2 e, _
44+
| _ , _ , Choice2Of2 e -> Choice2Of2 e
45+
3346
/// <summary>Flattens two nested Choice.</summary>
3447
/// <param name="source">The nested Choice.</param>
3548
/// <returns>A single Choice1Of2 of the value when it was nested with Choice1Of2s, or the Choice2Of2.</returns>

src/FSharpPlus/Extensions/Dictionary.fs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ module Dictionary =
7373
| Some vy -> dct.Add (k, f.Invoke (vx, vy))
7474
| None -> ()
7575
dct
76+
77+
/// <summary>Combines values from three Dictionaries using mapping function.</summary>
78+
/// <remarks>Keys that are not present on every Dictionary are dropped.</remarks>
79+
/// <param name="f">The mapping function.</param>
80+
/// <param name="x">First input Dictionary.</param>
81+
/// <param name="y">Second input Dictionary.</param>
82+
/// <param name="y">Third input Dictionary.</param>
83+
///
84+
/// <returns>The mapped Dictionary.</returns>
85+
let map3 f (x: Dictionary<'Key, 'T1>) (y: Dictionary<'Key, 'T2>) (z: Dictionary<'Key, 'T3>) =
86+
let dct = Dictionary<'Key, 'U> ()
87+
let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt f
88+
for KeyValue(k, vx) in x do
89+
match tryGetValue k y, tryGetValue k z with
90+
| Some vy, Some vz -> dct.Add (k, f.Invoke (vx, vy, vz))
91+
| _ , _ -> ()
92+
dct
7693

7794
/// <summary>Applies given function to each value of the given dictionary.</summary>
7895
/// <param name="f">The mapping function.</param>

src/FSharpPlus/Extensions/Lazy.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ module Lazy =
99

1010
/// <summary>Creates a Lazy value from a pair of Lazy values, using a mapping function to combine them.</summary>
1111
let map2 (mapping: 'T->'U->'V) (x: Lazy<'T>) (y: Lazy<'U>) = Lazy<_>.Create (fun () -> mapping x.Value y.Value)
12+
13+
/// <summary>Creates a Lazy value from three Lazy values, using a function to combine them.</summary>
14+
/// <param name="x">The first Lazy value.</param>
15+
/// <param name="y">The second Lazy value.</param>
16+
/// <param name="z">The third Lazy value.</param>
17+
///
18+
/// <returns>The combined value.</returns>
19+
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)
1220

1321
/// <summary>Applies a Lazy value to a Lazy function.</summary>
1422
/// <param name="f">The Lazy function.</param>

src/FSharpPlus/Extensions/List.fs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ module List =
3030

3131
/// Combines all values from the first list with the second, using the supplied mapping function.
3232
let lift2 f x1 x2 = List.allPairs x1 x2 |> List.map (fun (x, y) -> f x y)
33+
34+
/// <summary>Combines values from three list and calls a mapping function on this combination.</summary>
35+
/// <param name="f">Mapping function taking three element combination as input.</param>
36+
/// <param name="x1">First list.</param>
37+
/// <param name="x2">Second list.</param>
38+
/// <param name="x3">Third list.</param>
39+
///
40+
/// <returns>List with values returned from mapping function.</returns>
41+
let lift3 f x1 x2 x3 =
42+
List.allPairs x2 x3
43+
|> List.allPairs x1
44+
|> List.map (fun x -> (fst (snd x), snd (snd x), fst x))
45+
|> List.map (fun (x, y, z) -> f x y z)
3346

3447
/// Returns a list with all possible tails of the source list.
3548
let tails x = let rec loop = function [] -> [] | _::xs as s -> s::(loop xs) in loop x

src/FSharpPlus/Extensions/Map.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ module Map =
4646
| Some vy -> yield (k, f.Invoke (vx, vy))
4747
| None -> () }
4848

49+
/// <summary>Combines values from three maps using mapping function.</summary>
50+
/// <remarks>Keys that are not present on every Map are dropped.</remarks>
51+
/// <param name="f">The mapping function.</param>
52+
/// <param name="x">First input Map.</param>
53+
/// <param name="y">Second input Map.</param>
54+
/// <param name="y">Third input Map.</param>
55+
///
56+
/// <returns>The mapped Map.</returns>
57+
let mapValues3 f (x: Map<'Key, 'T1>) (y: Map<'Key, 'T2>) (z: Map<'Key, 'T3>) = Map <| seq {
58+
let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt f
59+
for KeyValue(k, vx) in x do
60+
match Map.tryFind k y, lazy Map.tryFind k z with
61+
| Some vy, Lazy (Some vz) -> yield (k, f.Invoke (vx, vy, vz))
62+
| _ , _ -> () }
63+
4964
/// <summary>Applies given function to each value of the given Map.</summary>
5065
/// <param name="f">The mapping function.</param>
5166
/// <param name="x">The input Map.</param>

src/FSharpPlus/Extensions/ResizeArray.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ module ResizeArray =
3333
/// Combines all values from the first ResizeArray with the second, using the supplied mapping function.
3434
let lift2 mapping (x1: ResizeArray<'T>) (x2: ResizeArray<'U>) = ResizeArray (Seq.lift2 mapping x1 x2)
3535

36+
/// <summary>Combines values from three ResizeArrays and calls a mapping function on this combination.</summary>
37+
/// <param name="f">Mapping function taking three element combination as input.</param>
38+
/// <param name="x1">First ResizeArray.</param>
39+
/// <param name="x2">Second ResizeArray.</param>
40+
/// <param name="x3">Third ResizeArray.</param>
41+
///
42+
/// <returns>ResizeArray with values returned from mapping function.</returns>
43+
let lift3 mapping (x1: ResizeArray<'T>) (x2: ResizeArray<'U>) (x3: ResizeArray<'V>) =
44+
ResizeArray (Seq.lift3 mapping x1 x2 x3)
45+
3646
/// Concatenates all elements, using the specified separator between each element.
3747
let intercalate (separator: _ []) (source: seq<_ []>) = source |> Seq.intercalate separator |> Seq.toArray
3848

0 commit comments

Comments
 (0)