Skip to content

Commit 699d664

Browse files
bvkatwijkpivovarit
andauthored
Add compose1, ..., composeN methods to FunctionN (#3016)
- [x] Add (generated) `composeN` method(s) to `Function`, adding a composition function to the nth parameter - `composeN` method name is numbered (e.g. `compose1`) to prevent ambiguity - [x] Add (generated) tests Open considerations: - `Function1#compose` now has a (virtually) duplicate `compose1`, is this an issue? - Should the input argument be `Function` or `Function1`? - This pattern and naming scheme could also be used to add `curryN`, `applyN` etc., --------- Co-authored-by: Grzegorz Piwowarek <gpiwowarek@gmail.com>
1 parent a430dea commit 699d664

35 files changed

+1653
-21
lines changed

vavr/generator/Generator.scala

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,35 @@ def generateMainClasses(): Unit = {
18911891
return v -> apply(before.apply(v));
18921892
}
18931893
""")}
1894+
1895+
${(1 to i).gen(j => {
1896+
val fName = s"before$j"
1897+
val fGeneric = "S"
1898+
val applicationArgs = (1 to i).gen(k => if (k == j) s"$fGeneric ${fGeneric.toLowerCase}" else s"T$k t$k")(", ")
1899+
val generics = (1 to i).gen(k => if (k == j) "S" else s"T$k")(", ")
1900+
val resultFunctionArgs = (j+1 to i).gen(k => s"T$k t$k")(", ")
1901+
val applyArgs = (1 to i).gen(k => if (k ==j) s"$fName.apply(${fGeneric.toLowerCase})" else s"t$k")(", ")
1902+
val variableApplyArgs = (j+1 to i).gen(k => s"t$k")(", ")
1903+
val docAdd = i match
1904+
case 1 => ""
1905+
case 2 => " and the other argument"
1906+
case _ => " and the other arguments"
1907+
xs"""
1908+
/$javadoc
1909+
* Returns a composed function that first applies the {@linkplain Function} {@code $fName} to the
1910+
* ${j.ordinal} argument and then applies this $className to the result$docAdd.
1911+
*
1912+
* @param <$fGeneric> argument type of $fName
1913+
* @param $fName the function applied before this
1914+
* @return a function composed of $fName and this
1915+
* @throws NullPointerException if $fName is null
1916+
*/
1917+
default <S> $className<$generics, R> compose$j(Function1<? super $fGeneric, ? extends T$j> $fName) {
1918+
Objects.requireNonNull($fName, "$fName is null");
1919+
return ($applicationArgs) -> apply($applyArgs);
1920+
}
1921+
"""
1922+
})("\n\n")}
18941923
}
18951924

18961925
${checked.gen(xs"""
@@ -3070,6 +3099,7 @@ def generateTestClasses(): Unit = {
30703099
def genFunctionTest(name: String, checked: Boolean)(im: ImportManager, packageName: String, className: String): String = {
30713100

30723101
val AtomicInteger = im.getType("java.util.concurrent.atomic.AtomicInteger")
3102+
val nested = im.getType("org.junit.jupiter.api.Nested")
30733103

30743104
val functionArgsDecl = (1 to i).gen(j => s"Object o$j")(", ")
30753105
val functionArgs = (1 to i).gen(j => s"o$j")(", ")
@@ -3414,15 +3444,27 @@ def generateTestClasses(): Unit = {
34143444
$assertThat(composed).isNotNull();
34153445
}
34163446

3417-
${(i == 1).gen(xs"""
3418-
@$test
3419-
public void shouldComposeWithCompose() {
3420-
final $name$i<$generics> f = ($functionArgs) -> null;
3421-
final ${name}1<Object, Object> before = o -> null;
3422-
final $name$i<$generics> composed = f.compose(before);
3423-
$assertThat(composed).isNotNull();
3424-
}
3425-
""")}
3447+
@Nested
3448+
class ComposeTests {
3449+
${(1 to i).gen(j =>
3450+
val genArgs = (1 to i).gen(k => "String")(", ")
3451+
val params = (1 to i).gen(k => s"String s$k")(", ")
3452+
val values = (1 to i).gen(k => if (k == j) "\"xx\"" else s"\"s$k\"")(", ")
3453+
val expected = (1 to i).gen(k => if (k == j) "XX" else s"s$k")("")
3454+
val concat = (1 to i).gen(k => s"s$k")(" + ")
3455+
xs"""
3456+
3457+
@$test
3458+
public void shouldCompose$j() ${checked.gen(" throws Throwable ")}{
3459+
final $name$i<$genArgs, String> concat = ($params) -> $concat;
3460+
final Function1<String, String> toUpperCase = String::toUpperCase;
3461+
assertThat(concat.compose$j(toUpperCase).apply($values)).isEqualTo(\"$expected\");
3462+
}
3463+
3464+
"""
3465+
)}
3466+
3467+
}
34263468

34273469
${(i == 0).gen(xs"""
34283470
@$test

vavr/src-gen/main/java/io/vavr/CheckedFunction1.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,20 @@ default <V> CheckedFunction1<V, R> compose(CheckedFunction1<? super V, ? extends
298298
Objects.requireNonNull(before, "before is null");
299299
return v -> apply(before.apply(v));
300300
}
301+
302+
/**
303+
* Returns a composed function that first applies the {@linkplain Function} {@code before1} to the
304+
* 1st argument and then applies this CheckedFunction1 to the result.
305+
*
306+
* @param <S> argument type of before1
307+
* @param before1 the function applied before this
308+
* @return a function composed of before1 and this
309+
* @throws NullPointerException if before1 is null
310+
*/
311+
default <S> CheckedFunction1<S, R> compose1(Function1<? super S, ? extends T1> before1) {
312+
Objects.requireNonNull(before1, "before1 is null");
313+
return (S s) -> apply(before1.apply(s));
314+
}
301315
}
302316

303317
interface CheckedFunction1Module {

vavr/src-gen/main/java/io/vavr/CheckedFunction2.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,33 @@ default <V> CheckedFunction2<T1, T2, V> andThen(CheckedFunction1<? super R, ? ex
294294
return (t1, t2) -> after.apply(apply(t1, t2));
295295
}
296296

297+
/**
298+
* Returns a composed function that first applies the {@linkplain Function} {@code before1} to the
299+
* 1st argument and then applies this CheckedFunction2 to the result and the other argument.
300+
*
301+
* @param <S> argument type of before1
302+
* @param before1 the function applied before this
303+
* @return a function composed of before1 and this
304+
* @throws NullPointerException if before1 is null
305+
*/
306+
default <S> CheckedFunction2<S, T2, R> compose1(Function1<? super S, ? extends T1> before1) {
307+
Objects.requireNonNull(before1, "before1 is null");
308+
return (S s, T2 t2) -> apply(before1.apply(s), t2);
309+
}
310+
311+
/**
312+
* Returns a composed function that first applies the {@linkplain Function} {@code before2} to the
313+
* 2nd argument and then applies this CheckedFunction2 to the result and the other argument.
314+
*
315+
* @param <S> argument type of before2
316+
* @param before2 the function applied before this
317+
* @return a function composed of before2 and this
318+
* @throws NullPointerException if before2 is null
319+
*/
320+
default <S> CheckedFunction2<T1, S, R> compose2(Function1<? super S, ? extends T2> before2) {
321+
Objects.requireNonNull(before2, "before2 is null");
322+
return (T1 t1, S s) -> apply(t1, before2.apply(s));
323+
}
297324
}
298325

299326
interface CheckedFunction2Module {

vavr/src-gen/main/java/io/vavr/CheckedFunction3.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,47 @@ default <V> CheckedFunction3<T1, T2, T3, V> andThen(CheckedFunction1<? super R,
311311
return (t1, t2, t3) -> after.apply(apply(t1, t2, t3));
312312
}
313313

314+
/**
315+
* Returns a composed function that first applies the {@linkplain Function} {@code before1} to the
316+
* 1st argument and then applies this CheckedFunction3 to the result and the other arguments.
317+
*
318+
* @param <S> argument type of before1
319+
* @param before1 the function applied before this
320+
* @return a function composed of before1 and this
321+
* @throws NullPointerException if before1 is null
322+
*/
323+
default <S> CheckedFunction3<S, T2, T3, R> compose1(Function1<? super S, ? extends T1> before1) {
324+
Objects.requireNonNull(before1, "before1 is null");
325+
return (S s, T2 t2, T3 t3) -> apply(before1.apply(s), t2, t3);
326+
}
327+
328+
/**
329+
* Returns a composed function that first applies the {@linkplain Function} {@code before2} to the
330+
* 2nd argument and then applies this CheckedFunction3 to the result and the other arguments.
331+
*
332+
* @param <S> argument type of before2
333+
* @param before2 the function applied before this
334+
* @return a function composed of before2 and this
335+
* @throws NullPointerException if before2 is null
336+
*/
337+
default <S> CheckedFunction3<T1, S, T3, R> compose2(Function1<? super S, ? extends T2> before2) {
338+
Objects.requireNonNull(before2, "before2 is null");
339+
return (T1 t1, S s, T3 t3) -> apply(t1, before2.apply(s), t3);
340+
}
341+
342+
/**
343+
* Returns a composed function that first applies the {@linkplain Function} {@code before3} to the
344+
* 3rd argument and then applies this CheckedFunction3 to the result and the other arguments.
345+
*
346+
* @param <S> argument type of before3
347+
* @param before3 the function applied before this
348+
* @return a function composed of before3 and this
349+
* @throws NullPointerException if before3 is null
350+
*/
351+
default <S> CheckedFunction3<T1, T2, S, R> compose3(Function1<? super S, ? extends T3> before3) {
352+
Objects.requireNonNull(before3, "before3 is null");
353+
return (T1 t1, T2 t2, S s) -> apply(t1, t2, before3.apply(s));
354+
}
314355
}
315356

316357
interface CheckedFunction3Module {

vavr/src-gen/main/java/io/vavr/CheckedFunction4.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,61 @@ default <V> CheckedFunction4<T1, T2, T3, T4, V> andThen(CheckedFunction1<? super
330330
return (t1, t2, t3, t4) -> after.apply(apply(t1, t2, t3, t4));
331331
}
332332

333+
/**
334+
* Returns a composed function that first applies the {@linkplain Function} {@code before1} to the
335+
* 1st argument and then applies this CheckedFunction4 to the result and the other arguments.
336+
*
337+
* @param <S> argument type of before1
338+
* @param before1 the function applied before this
339+
* @return a function composed of before1 and this
340+
* @throws NullPointerException if before1 is null
341+
*/
342+
default <S> CheckedFunction4<S, T2, T3, T4, R> compose1(Function1<? super S, ? extends T1> before1) {
343+
Objects.requireNonNull(before1, "before1 is null");
344+
return (S s, T2 t2, T3 t3, T4 t4) -> apply(before1.apply(s), t2, t3, t4);
345+
}
346+
347+
/**
348+
* Returns a composed function that first applies the {@linkplain Function} {@code before2} to the
349+
* 2nd argument and then applies this CheckedFunction4 to the result and the other arguments.
350+
*
351+
* @param <S> argument type of before2
352+
* @param before2 the function applied before this
353+
* @return a function composed of before2 and this
354+
* @throws NullPointerException if before2 is null
355+
*/
356+
default <S> CheckedFunction4<T1, S, T3, T4, R> compose2(Function1<? super S, ? extends T2> before2) {
357+
Objects.requireNonNull(before2, "before2 is null");
358+
return (T1 t1, S s, T3 t3, T4 t4) -> apply(t1, before2.apply(s), t3, t4);
359+
}
360+
361+
/**
362+
* Returns a composed function that first applies the {@linkplain Function} {@code before3} to the
363+
* 3rd argument and then applies this CheckedFunction4 to the result and the other arguments.
364+
*
365+
* @param <S> argument type of before3
366+
* @param before3 the function applied before this
367+
* @return a function composed of before3 and this
368+
* @throws NullPointerException if before3 is null
369+
*/
370+
default <S> CheckedFunction4<T1, T2, S, T4, R> compose3(Function1<? super S, ? extends T3> before3) {
371+
Objects.requireNonNull(before3, "before3 is null");
372+
return (T1 t1, T2 t2, S s, T4 t4) -> apply(t1, t2, before3.apply(s), t4);
373+
}
374+
375+
/**
376+
* Returns a composed function that first applies the {@linkplain Function} {@code before4} to the
377+
* 4th argument and then applies this CheckedFunction4 to the result and the other arguments.
378+
*
379+
* @param <S> argument type of before4
380+
* @param before4 the function applied before this
381+
* @return a function composed of before4 and this
382+
* @throws NullPointerException if before4 is null
383+
*/
384+
default <S> CheckedFunction4<T1, T2, T3, S, R> compose4(Function1<? super S, ? extends T4> before4) {
385+
Objects.requireNonNull(before4, "before4 is null");
386+
return (T1 t1, T2 t2, T3 t3, S s) -> apply(t1, t2, t3, before4.apply(s));
387+
}
333388
}
334389

335390
interface CheckedFunction4Module {

vavr/src-gen/main/java/io/vavr/CheckedFunction5.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,75 @@ default <V> CheckedFunction5<T1, T2, T3, T4, T5, V> andThen(CheckedFunction1<? s
350350
return (t1, t2, t3, t4, t5) -> after.apply(apply(t1, t2, t3, t4, t5));
351351
}
352352

353+
/**
354+
* Returns a composed function that first applies the {@linkplain Function} {@code before1} to the
355+
* 1st argument and then applies this CheckedFunction5 to the result and the other arguments.
356+
*
357+
* @param <S> argument type of before1
358+
* @param before1 the function applied before this
359+
* @return a function composed of before1 and this
360+
* @throws NullPointerException if before1 is null
361+
*/
362+
default <S> CheckedFunction5<S, T2, T3, T4, T5, R> compose1(Function1<? super S, ? extends T1> before1) {
363+
Objects.requireNonNull(before1, "before1 is null");
364+
return (S s, T2 t2, T3 t3, T4 t4, T5 t5) -> apply(before1.apply(s), t2, t3, t4, t5);
365+
}
366+
367+
/**
368+
* Returns a composed function that first applies the {@linkplain Function} {@code before2} to the
369+
* 2nd argument and then applies this CheckedFunction5 to the result and the other arguments.
370+
*
371+
* @param <S> argument type of before2
372+
* @param before2 the function applied before this
373+
* @return a function composed of before2 and this
374+
* @throws NullPointerException if before2 is null
375+
*/
376+
default <S> CheckedFunction5<T1, S, T3, T4, T5, R> compose2(Function1<? super S, ? extends T2> before2) {
377+
Objects.requireNonNull(before2, "before2 is null");
378+
return (T1 t1, S s, T3 t3, T4 t4, T5 t5) -> apply(t1, before2.apply(s), t3, t4, t5);
379+
}
380+
381+
/**
382+
* Returns a composed function that first applies the {@linkplain Function} {@code before3} to the
383+
* 3rd argument and then applies this CheckedFunction5 to the result and the other arguments.
384+
*
385+
* @param <S> argument type of before3
386+
* @param before3 the function applied before this
387+
* @return a function composed of before3 and this
388+
* @throws NullPointerException if before3 is null
389+
*/
390+
default <S> CheckedFunction5<T1, T2, S, T4, T5, R> compose3(Function1<? super S, ? extends T3> before3) {
391+
Objects.requireNonNull(before3, "before3 is null");
392+
return (T1 t1, T2 t2, S s, T4 t4, T5 t5) -> apply(t1, t2, before3.apply(s), t4, t5);
393+
}
394+
395+
/**
396+
* Returns a composed function that first applies the {@linkplain Function} {@code before4} to the
397+
* 4th argument and then applies this CheckedFunction5 to the result and the other arguments.
398+
*
399+
* @param <S> argument type of before4
400+
* @param before4 the function applied before this
401+
* @return a function composed of before4 and this
402+
* @throws NullPointerException if before4 is null
403+
*/
404+
default <S> CheckedFunction5<T1, T2, T3, S, T5, R> compose4(Function1<? super S, ? extends T4> before4) {
405+
Objects.requireNonNull(before4, "before4 is null");
406+
return (T1 t1, T2 t2, T3 t3, S s, T5 t5) -> apply(t1, t2, t3, before4.apply(s), t5);
407+
}
408+
409+
/**
410+
* Returns a composed function that first applies the {@linkplain Function} {@code before5} to the
411+
* 5th argument and then applies this CheckedFunction5 to the result and the other arguments.
412+
*
413+
* @param <S> argument type of before5
414+
* @param before5 the function applied before this
415+
* @return a function composed of before5 and this
416+
* @throws NullPointerException if before5 is null
417+
*/
418+
default <S> CheckedFunction5<T1, T2, T3, T4, S, R> compose5(Function1<? super S, ? extends T5> before5) {
419+
Objects.requireNonNull(before5, "before5 is null");
420+
return (T1 t1, T2 t2, T3 t3, T4 t4, S s) -> apply(t1, t2, t3, t4, before5.apply(s));
421+
}
353422
}
354423

355424
interface CheckedFunction5Module {

0 commit comments

Comments
 (0)