Reputation: 9559
I find my Java knowledge out-of-date with Java 8, and there are many new language features I am trying to learn. One of them is Functions, specifically the compose
and andThen
methods.
I have written a trivial experiment to test the reversibility of compose
and andThen
:
/** Wraps a value
*/
public class Wrap<T> {
private final T value;
private Wrap(T value) {
this.value = value;
}
public static <T> Wrap<T> of(T value) {
return new Wrap<>(value);
}
}
static void functions() {
Function<Integer,String> itos = i->"'"+i+"'";
Function<String,Wrap<String>> stow = s->Wrap.of(s);
Function<Integer,Wrap<String>> itow = itos.andThen(stow);
Function<Integer,Wrap<String>> itow2 = stow.compose(itos);
System.out.println(itow.apply(3));
System.out.println(itow2.apply(3));
}
In the above code, as expected, the 2 Functions itow
and itow2
seem to be equivalent. But are they actually equivalent? Do the have the same result somewhat by accident in this case?
The thought occurs that both compose
and andThen
methods exist for a reason, and that Functions or BiFunctions might not always be reversible in this way. Can you think of any cases where the reversibility would not apply?
Upvotes: 21
Views: 37382
Reputation: 51
if order is important than it makes difference
Function<Integer,Integer> sum2 = integer -> integer + 2;
Function<Integer,Integer> multiply5 = integer -> integer * 5;
System.out.println(sum2.andThen(multiply5).apply(3));
System.out.println(sum2.compose(multiply5).apply(3));
output:
25
17
Upvotes: 0
Reputation: 1356
They are different in respect to the sequence in which they work.
A Simple example -
Program 1:
Function<String, String> x = myString -> myString + ",\tx function";
Function<String, String> y = myString -> myString + ",\ty function";
x = x.andThen(y);
System.out.println(x.apply("Hello"));
Output -
Hello, x function, y function
Here, x function
function gets executed and then y function
gets executed
Program 2:
Function<String, String> x = myString -> myString + ",\tx function";
Function<String, String> y = myString -> myString + ",\ty function";
y = y.compose(x);
System.out.println(y.apply("Hello"));
Output -
Hello, x function, y function
Here, y function
composes x function
first and then executes itself.
Thus, although outcome is same, the sequence of execution is different.
A second example -
final double startNumber = 3.5;
final Function<Double, Double> cubeRoot = x -> {
System.out.println("Calculating cube root");
return Math.pow(x, 1.0/3);
};
final Function<Double, Double> square = x -> {
System.out.println("Calculating square");
return Math.pow(x, 2);
};
final Function<Double, Long> round = x -> {
System.out.println("Rounding");
return Math.round(x);
};
Long result = cubeRoot.andThen(square).andThen(round).apply(startNumber);
System.out.println("Result: " + result);
result = round.compose(square).compose(cubeRoot).apply(startNumber);
System.out.println("Result: " + result);
Output -
Calculating cube root
Calculating square
Rounding
Result: 2
Calculating cube root
Calculating square
Rounding
Result: 2
In cubeRoot.andThen(square).andThen(round).apply(startNumber)
first cubeRoot
is done, then square
is done, then round
is done.
In round.compose(square).compose(cubeRoot).apply(startNumber)
first cubeRoot
is composed and performed, then square
is composed and performed, then round
is composed and performed.
Thus, although outcome is same here too, the sequence of execution is different.
Look at the following code -
import java.util.function.Function;
public class App {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
int x = 1;
String string1 = "Neha Wakes up";
String string2 = "Brushes teeth";
String string3 = "Goes to toilet";
String string4 = "Takes a shower";
String string5 = "Feeds the cat";
String string6 = "Cleans the litter box";
String string7 = "Eats breakfast";
String string8 = "Goes for work";
Function<Integer, Integer> step1 = a -> {
stringBuilder.append(string1).append("\n");
return a+1;
};
Function<Integer, Integer> step2 = a -> {
stringBuilder.append(string2).append("\n");
return a+1;
};
Function<Integer, Integer> step3 = a -> {
stringBuilder.append(string3).append("\n");
return a+1;
};
Function<Integer, Integer> step4 = a -> {
stringBuilder.append(string4).append("\n");
return a+1;
};
Function<Integer, Integer> step5 = a -> {
stringBuilder.append(string5).append("\n");
return a+1;
};
Function<Integer, Integer> step6 = a -> {
stringBuilder.append(string6).append("\n");
return a+1;
};
Function<Integer, Integer> step7 = a -> {
stringBuilder.append(string7).append("\n");
return a+1;
};
Function<Integer, Integer> step8 = a -> {
stringBuilder.append(string8).append("\n");
return a+1;
};
int num1 = step1.andThen(step2).andThen(step3).andThen(step4).andThen(step5).andThen(step6).andThen(step7).andThen(step8).apply(x);
System.out.println(num1);
System.out.println(stringBuilder.toString());
stringBuilder.setLength(0);
int num2 = step1.compose(step2).compose(step3).compose(step4).compose(step5).compose(step6).compose(step7).compose(step8).apply(x);
System.out.println(num2);
System.out.println(stringBuilder.toString());
}
}
Output -
9
Neha Wakes up
Brushes teeth
Goes to toilet
Takes a shower
Feeds the cat
Cleans the litter box
Eats breakfast
Goes for work
9
Goes for work
Eats breakfast
Cleans the litter box
Feeds the cat
Takes a shower
Goes to toilet
Brushes teeth
Neha Wakes up
This further proves my point. Here in both cases the numerical output is same that is 9. But in first case andThen()
executes the chain from front to end, while compose()
composes the chain first from end to front and then executes it.
Upvotes: 4
Reputation: 41213
The source code for Function
is freely available:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
From this it's pretty clear that a.andThen(b)
is equivalent to b.compose(a)
.
It's perhaps even clearer if we make the implied this
explicit:
return (V v) -> this .apply(before.apply(v)); //compose
return (T t) -> after.apply(this .apply(t)); // andThen
You may ask, if they're equivalent, why do both exist?
Well, they're only there as a convenience in the first place. You could manage without either of them:
a.andThen(b)
... is of course equivalent to:
(x) -> b.apply(a.apply(x));
So, given that they're there as a convenience, each is convenient in different circumstances. You can choose which is most expressive in a particular situation.
In many functional languages that don't use the method
construct, the equivalent to compose
and andThen
are compose
and pipe
, which differ only in the order the composed functions are applied.
In those languages, these are all equivalent:
fn = x => a(b(c(x)))
fn = compose(a, b, c)
fn = pipe(c, b, a)
You can see that with compose
the functions are written in the same order as they would be if you wrote them nested.
With pipe
the functions are written in the order they'll be applied.
Which of those is appropriate depends on circumstances, how the coder's brain is working when they write it, and how the coder would like the reader's brain to work when they read it.
One style guide for Ramda suggests that one-liners should use pipe
whereas multi-line compositions should use compose
:
// one-liner
const toDocument = pipe(fillTemplate, addTitles, toPdf)
// multi-line
const toDocument = compose(
toPdf,
addTitles,
fillTemplate
)
Java's .compose()
and .andThen()
are just the Java equivalent of this. Use the one that makes the code seem easiest to you. In terms of performance there is no significant difference.
Upvotes: 5
Reputation: 298123
While a.andThen(b)
is equivalent to b.compose(a)
, there’s a practical difference at the use side, when only one of these two functions is an already existing function.
Suppose, you have the already existing function
Function<Integer, String> iToHex = i -> "'" + Integer.toHexString(i) + "'";
and then, you want to chain a string transformation, e.g.
Function<Integer, String> itoUpperHex = iToHex.andThen(String::toUpperCase);
andThen
obviously is much more convenient, compared to
Function<Integer,String> itoUpperHex
= ((Function<String,String>)String::toUpperCase).compose(iToHex);
On the other hand, if your already existing function is
Function<String, String> quote = s -> "'" + s + "'";
and you want to create the quoted hex function,
Function<Integer, String> iToHex = quote.compose(Integer::toHexString);
will be much more convenient, compared to
Function<Integer, String> iToHex = ((Function<Integer, String>) Integer::toHexString).andThen(quote);
So the decision for either method depends on which function already exists. If both are already existing functions, it doesn’t matter which method you use (unless you suspect that one of these functions might have overridden these methods to perform a more efficient composition). If you have no existing function as starting point, there is no reason to use any of these methods, as you can write the composed expression as a single lambda expression.
Upvotes: 40
Reputation: 119
Both are similar except the sequence how it executes the functions, refer example below-
import java.util.function.Function;
public class FunctionThenComposeTest {
public static void main(String[] xavis_args) {
Function<Integer, Integer> f1 = i -> {
System.out.println("Inside F1");
return i * i;
};
Function<Integer, Integer> f2 = i -> {
System.out.println("Inside F2");
return i * i;
};
System.out.println("And Then method");
System.out.println(f1.andThen(f2).apply(2));// Execute f1 function first, take the result of f1 and then apply it to f2 function(The result of f1 function will be the input to f2 function)
System.out.println("Compose method");
System.out.println(f1.compose(f2).apply(2));// First execute f2 function and then take the result of f2 and execute f1 (The result of f2 will be the input to f1 function)
}
}
OUTPUT
And Then method
Inside F1
Inside F2
16
Compose method
Inside F2
Inside F1
16
Upvotes: 3
Reputation: 211
below example shows that andThen()
& compose()
are contradictory methods. its produces opposite result to each other.(see output for more clarification)
f1.andThen(f2).apply(10);
=> first execute f1.apply(10)
method. based on output first method, f2.apply(result_of_f1_method)
method execute.
f1.compose(f2).apply(10);
=> here just opposite to andThen()
method.
first f2.apply()
and then f1.apply(output_of_f2_method)
execute.
public class FunctionDemo {
public static void main(String[] args) {
Function<Integer, Integer> f1 = num -> (num - 4);
Function<Integer, Integer> f2 = num -> (num * 2);
// Using andThen() method
int a=f1.andThen(f2).apply(10);
System.out.println(a);// Output - 12
//Using compose function
int b=f1.compose(f2).apply(10);
System.out.println(b);// Output - 16
}
}
Upvotes: 2
Reputation: 20467
I'd say they are equivalent, see the implementation:
x.compose(y)
= x.apply(y.apply(...))
and
y.andThen(x)
= x.apply(y.apply(...))
From Function.java
(I added this.
for clarity):
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> this.apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(this.apply(t));
}
Upvotes: 2
Reputation: 48404
From the Javadocs, there's a clear difference between compose
and andThen
:
Returns a composed function that first applies the before function to its input, and then applies this function to the result.
Returns a composed function that first applies this function to its input, and then applies the after function to the result.
As such, reversibility will depend on the implementation of your function.
In your case, itow
and itow2
are just two alternative ways of expressing the same: "run itos
, then stow
in this order".
Upvotes: 7
Reputation: 3554
They are equivalent.
Or in other words: x.andThen(y)
is the same as y.compose(x)
.
Upvotes: 7