Skip to content

Commit b3f84a2

Browse files
committed
- deprecating Fn1#then
- renaming Fn1#adapt and Fn2#adapt to Fn1#fn1 and Fn2#fn2 via deprecation - Fn2#compose now returns another Fn2 - Fn2#fn2 overload to adapt curried Fn1- - adding Fn1#andThen overload to support composition with different arity functions
1 parent c0420a4 commit b3f84a2

6 files changed

Lines changed: 128 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/).
55

66
## [Unreleased]
7+
### Deprecated
8+
- `Fn1#then` in favor of `Fn1#andThen` (redundant)
9+
- `Fn1#adapt` in favor of `Fn1#fn1` (rename)
10+
- `Fn2#adapt` in favor of `Fn2#fn2` (rename)
11+
12+
### Added
13+
- `Fn1#andThen` overload to support composition with `Bifunction`
14+
- `Fn1#compose` overload to support composition with `Bifunction`
715

816
## [1.6.2] - 2017-08-20
917
### Fixed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ Every function in lambda is [curried](https://www.wikiwand.com/en/Currying), so
8181
```Java
8282
Fn1<Iterable<Integer>, Integer> sumOfEvenIncrementsFn =
8383
map((Integer x) -> x + 1)
84-
.then(filter(x -> x % 2 == 0))
85-
.then(reduceLeft((x, y) -> x + y));
84+
.andThen(filter(x -> x % 2 == 0))
85+
.andThen(reduceLeft((x, y) -> x + y));
8686

8787
Integer sumOfEvenIncrements = sumOfEvenIncrementsFn.apply(asList(1, 2, 3, 4, 5));
8888
//-> 12
@@ -120,7 +120,7 @@ Let's compose two functions:
120120
Fn1<Integer, Integer> add = x -> x + 1;
121121
Fn1<Integer, Integer> subtract = x -> x -1;
122122

123-
Fn1<Integer, Integer> noOp = add.then(subtract);
123+
Fn1<Integer, Integer> noOp = add.andThen(subtract);
124124
// same as
125125
Fn1<Integer, Integer> alsoNoOp = subtract.compose(add);
126126
```

src/main/java/com/jnape/palatable/lambda/functions/Fn1.java

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.jnape.palatable.lambda.functor.Applicative;
44
import com.jnape.palatable.lambda.functor.Profunctor;
55

6+
import java.util.function.BiFunction;
67
import java.util.function.Function;
78

89
/**
@@ -30,7 +31,9 @@ public interface Fn1<A, B> extends Applicative<B, Fn1<A, ?>>, Profunctor<A, B, F
3031
* @param f the function to invoke with this function's return value
3132
* @param <C> the return type of the next function to invoke
3233
* @return a function representing the composition of this function and f
34+
* @deprecated in favor of {@link Fn1#andThen(Function)}
3335
*/
36+
@Deprecated
3437
default <C> Fn1<A, C> then(Function<? super B, ? extends C> f) {
3538
return fmap(f);
3639
}
@@ -41,34 +44,47 @@ default <C> Fn1<A, C> then(Function<? super B, ? extends C> f) {
4144
* @param <C> the return type of the next function to invoke
4245
* @param f the function to invoke with this function's return value
4346
* @return a function representing the composition of this function and f
44-
* @see Fn1#then(Function)
4547
*/
4648
@Override
47-
@SuppressWarnings("unchecked")
4849
default <C> Fn1<A, C> fmap(Function<? super B, ? extends C> f) {
49-
return (Fn1<A, C>) Applicative.super.fmap(f);
50+
return Applicative.super.<C>fmap(f).coerce();
5051
}
5152

53+
/**
54+
* {@inheritDoc}
55+
*/
5256
@Override
5357
default <C> Fn1<A, C> pure(C c) {
5458
return __ -> c;
5559
}
5660

61+
/**
62+
* {@inheritDoc}
63+
*/
5764
@Override
5865
default <C> Fn1<A, C> zip(Applicative<Function<? super B, ? extends C>, Fn1<A, ?>> appFn) {
5966
return a -> appFn.<Fn1<A, Function<? super B, ? extends C>>>coerce().apply(a).apply(apply(a));
6067
}
6168

69+
/**
70+
* {@inheritDoc}
71+
*/
6272
@SuppressWarnings("unchecked")
6373
default <C> Fn1<A, C> zip(Fn2<A, B, C> appFn) {
6474
return zip((Fn1<A, Function<? super B, ? extends C>>) (Object) appFn);
6575
}
6676

77+
/**
78+
* {@inheritDoc}
79+
*/
6780
@Override
6881
default <C> Fn1<A, C> discardL(Applicative<C, Fn1<A, ?>> appB) {
6982
return Applicative.super.discardL(appB).coerce();
7083
}
7184

85+
/**
86+
* {@inheritDoc}
87+
*/
7288
@Override
7389
default <C> Fn1<A, B> discardR(Applicative<C, Fn1<A, ?>> appB) {
7490
return Applicative.super.discardR(appB).coerce();
@@ -127,6 +143,32 @@ default <Z> Fn1<Z, B> compose(Function<? super Z, ? extends A> before) {
127143
return z -> apply(before.apply(z));
128144
}
129145

146+
/**
147+
* Right-to-left composition between different arity functions. Preserves highest arity in the return type,
148+
* specialized to lambda types (in this case, {@link BiFunction} -&gt; {@link Fn2}).
149+
*
150+
* @param before the function to pass its return value to this function's input
151+
* @param <Y> the resulting function's first argument type
152+
* @param <Z> the resulting function's second argument type
153+
* @return a new function from Y x Z to B
154+
*/
155+
default <Y, Z> Fn2<Y, Z, B> compose(BiFunction<? super Y, ? super Z, ? extends A> before) {
156+
return (y, z) -> apply(before.apply(y, z));
157+
}
158+
159+
/**
160+
* Left-to-right composition between different arity functions. Preserves highest arity in the return type,
161+
* specialized to lambda types (in this case, {@link BiFunction} -&gt; {@link Fn2}).
162+
*
163+
* @param after the function to invoke on this function's return value
164+
* @param <C> the resulting function's second argument type
165+
* @param <D> the resulting function's return type
166+
* @return a new function from A x C to D
167+
*/
168+
default <C, D> Fn2<A, C, D> andThen(BiFunction<? super B, ? super C, ? extends D> after) {
169+
return (a, c) -> after.apply(apply(a), c);
170+
}
171+
130172
/**
131173
* Override of {@link Function#andThen(Function)}, returning an instance of <code>Fn1</code> for compatibility.
132174
* Left-to-right composition.
@@ -148,8 +190,23 @@ default <C> Fn1<A, C> andThen(Function<? super B, ? extends C> after) {
148190
* @param <A> the input argument type
149191
* @param <B> the output type
150192
* @return the Fn1
193+
* @deprecated in favor of {@link Fn1#fn1(Function)}
151194
*/
195+
@Deprecated
152196
static <A, B> Fn1<A, B> adapt(Function<A, B> function) {
153197
return function::apply;
154198
}
199+
200+
/**
201+
* Static factory method for wrapping a {@link Function} in an {@link Fn1}. Useful for avoid explicit casting when
202+
* using method references as {@link Fn1}s.
203+
*
204+
* @param function the function to adapt
205+
* @param <A> the input argument type
206+
* @param <B> the output type
207+
* @return the Fn1
208+
*/
209+
static <A, B> Fn1<A, B> fn1(Function<A, B> function) {
210+
return function::apply;
211+
}
155212
}

src/main/java/com/jnape/palatable/lambda/functions/Fn2.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
44

55
import java.util.function.BiFunction;
6+
import java.util.function.Function;
67

78
/**
89
* A function taking two arguments. Note that defining <code>Fn2</code> in terms of <code>Fn1</code> provides a
@@ -26,6 +27,18 @@ public interface Fn2<A, B, C> extends Fn1<A, Fn1<B, C>> {
2627
*/
2728
C apply(A a, B b);
2829

30+
/**
31+
* Same as normal composition, except that the result is an instance of <code>Fn2</code> for convenience.
32+
*
33+
* @param before the function who's return value is this function's argument
34+
* @param <Z> the new argument type
35+
* @return a new Fn2&lt;Z,B,C&gt;
36+
*/
37+
@Override
38+
default <Z> Fn2<Z, B, C> compose(Function<? super Z, ? extends A> before) {
39+
return fn2(Fn1.super.compose(before));
40+
}
41+
2942
/**
3043
* Partially apply this function by passing its first argument.
3144
*
@@ -74,8 +87,37 @@ default BiFunction<A, B, C> toBiFunction() {
7487
* @param <B> the second input argument type
7588
* @param <C> the output type
7689
* @return the Fn2
90+
* @deprecated in favor of {@link Fn2#fn2(BiFunction)}
7791
*/
92+
@Deprecated
7893
static <A, B, C> Fn2<A, B, C> adapt(BiFunction<A, B, C> biFunction) {
7994
return biFunction::apply;
8095
}
96+
97+
/**
98+
* Static factory method for wrapping a {@link BiFunction} in an {@link Fn2}. Useful for avoid explicit casting when
99+
* using method references as {@link Fn2}s.
100+
*
101+
* @param biFunction the biFunction to adapt
102+
* @param <A> the first input argument type
103+
* @param <B> the second input argument type
104+
* @param <C> the output type
105+
* @return the Fn2
106+
*/
107+
static <A, B, C> Fn2<A, B, C> fn2(BiFunction<A, B, C> biFunction) {
108+
return biFunction::apply;
109+
}
110+
111+
/**
112+
* Static factory method for wrapping a curried {@link Fn1} in an {@link Fn2}.
113+
*
114+
* @param curriedFn1 the curried fn1 to adapt
115+
* @param <A> the first input argument type
116+
* @param <B> the second input argument type
117+
* @param <C> the output type
118+
* @return the Fn2
119+
*/
120+
static <A, B, C> Fn2<A, B, C> fn2(Fn1<A, Fn1<B, C>> curriedFn1) {
121+
return (a, b) -> curriedFn1.apply(a).apply(b);
122+
}
81123
}

src/test/java/com/jnape/palatable/lambda/functions/Fn1Test.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
import java.util.function.Function;
1212

13-
import static org.hamcrest.MatcherAssert.assertThat;
14-
import static org.hamcrest.core.Is.is;
1513
import static org.junit.Assert.assertEquals;
1614

1715
@RunWith(Traits.class)
@@ -32,16 +30,8 @@ public void profunctorProperties() {
3230
}
3331

3432
@Test
35-
public void thenIsJustAnAliasForFmap() {
36-
Fn1<Integer, Integer> add2 = integer -> integer + 2;
37-
Fn1<Integer, String> toString = Object::toString;
38-
39-
assertThat(add2.then(toString).apply(2), is(toString.apply(add2.apply(2))));
40-
}
41-
42-
@Test
43-
public void adapt() {
33+
public void fn1() {
4434
Function<String, Integer> parseInt = Integer::parseInt;
45-
assertEquals((Integer) 1, Fn1.adapt(parseInt).apply("1"));
35+
assertEquals((Integer) 1, Fn1.fn1(parseInt).apply("1"));
4636
}
4737
}

src/test/java/com/jnape/palatable/lambda/functions/Fn2Test.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static org.hamcrest.core.Is.is;
99
import static org.junit.Assert.assertEquals;
1010
import static org.junit.Assert.assertThat;
11+
import static org.junit.Assert.assertTrue;
1112

1213
public class Fn2Test {
1314

@@ -29,15 +30,24 @@ public void uncurries() {
2930
assertThat(CHECK_LENGTH.uncurry().apply(tuple("abc", 3)), is(true));
3031
}
3132

33+
@Test
34+
@SuppressWarnings("ConstantConditions")
35+
public void composePreservesTypeSpecificity() {
36+
assertTrue(CHECK_LENGTH.compose(Object::toString) instanceof Fn2);
37+
}
38+
3239
@Test
3340
public void toBiFunction() {
3441
BiFunction<String, Integer, Boolean> biFunction = CHECK_LENGTH.toBiFunction();
3542
assertEquals(true, biFunction.apply("abc", 3));
3643
}
3744

3845
@Test
39-
public void adapt() {
40-
BiFunction<String, String, String> format = String::format;
41-
assertEquals("foo bar", Fn2.adapt(format).apply("foo %s", "bar"));
46+
public void fn2() {
47+
BiFunction<String, String, String> biFunction = String::format;
48+
assertEquals("foo bar", Fn2.fn2(biFunction).apply("foo %s", "bar"));
49+
50+
Fn1<String, Fn1<String, String>> curriedFn1 = (x) -> (y) -> String.format(x, y);
51+
assertEquals("foo bar", Fn2.fn2(curriedFn1).apply("foo %s", "bar"));
4252
}
4353
}

0 commit comments

Comments
 (0)