This is just a theorical question with no concrete application.
I have the following method which I will not touch. It could (if possible at all) be used as a BiConsumer.
void doSmallThing(A a, B b) {
// do something with a and b.
}
void doBigThing(List<A> as, B b) {
// What to do?
}
How can I iterate on as while keeping b constant and use this::doSmallThing in doBigThing?
Of course the following doesn't work.
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(this::doSmallThing);
}
The following works nice and is actually what I use everyday.
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(a -> doSmallThing(a, b));
}
The following also works well, but is a bit more tricky.
Consumer<A> doSmallThingWithFixedB(B b) {
return (a) -> doSmallThing(a, b);
}
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(doSmallThingWithFixedB(b))
}
But all of those solutions don't get the simplicity of the Consumer case. So is there anything simple that exists for BiConsumer?
You want to "bind" the function argument. Unfortunately there's no built-in mechanism to do this in Java 8 (except binding the object for instance methods like this::). You may generalize your doSmallThingWithFixedB method like this:
public class Bind {
public static <A, B> Consumer<A> bindLast(BiConsumer<A, B> fn, B b) {
return a -> fn.accept(a, b);
}
public static <A, B> Consumer<B> bindFirst(BiConsumer<A, B> fn, A a) {
return b -> fn.accept(a, b);
}
}
And use:
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(Bind.bindLast(this::doSmallThing, b));
}
Probably there's some third-party library which already contains such methods. However using explicit lambda seems ok for me. You should not try to express everything with method references.
BiConsumers are used when iterating over Map entries for example:
Map<A, B> map = ...;
map.forEach(this::doSomething);
Stream.collect() also takes BiConsumers as arguments, but it's used less often than an iteration on map entries.
Add a method doSmallThingto B:
class B {
public void doSmallThing(A a) {
YourClass.doSmallThing(a, this); // You may want to inline this.
}
}
and call it from doBigThing:
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(b::doSmallThing);
}
Related
I'm trying to understand how Either is implemented. I've gotten stuck at chaining together multiple functions in a way that allows returning a different Left value during flatMap. I can't work out how it is possible within the type system.
Minimal Either example code:
public class Either<A,B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C> Either<A, C> flatMap(Function<B, Either<A,C>> f) {
if (this.isRight()) return f.apply(this.right);
else return Either.left(this.left);
}
// map and other useful functions....
I originally thought I'd be able to map to different Left values, which would allow returning the relevant error at each point.
So, for instance, given these functions:
public static Either<Foo, String> doThing() {
return Either.right("foo");
}
public static Either<Bar, String> doThing2(String text) {
return (text.equals("foo"))
? Either.right("Yay!")
: Either.left(new Bar("Grr..."));
}
public static Either<Baz, String> doThing3() {
return (text.equals("Yay!"))
? Either.right("Hooray!")
: Either.left(new Baz("Oh no!!"));
}
I thought I'd be able to do
doThing().flatMap(x -> doThing2()).flatMap(y -> doThing3())
However, the compiler flags this as impossible.
After some studying of the code, I realized that it's due to my <A,B> generic parameters.
flatMap has two different cases:
the case where we map the right side
the case where we pass through the left value
So, if my goal is to enable sometimes returning different Left values from flatMap, then my two generic variables <A,B> don't work, because if case 1 executes and my function changes A, then case 2 is invalid, because A != A'. The act of applying a function to the right side may have changed the Left side to a different type.
All this leads me to these questions:
Is my expectation for the behavior of the Either type incorrect?
Is it possible to return different Left types during a flatMap operation?
if so, how do you get the types to work out?
There isn't a sensible flatMap() function like you want, due to parametricity. Consider:
Either<Foo, String> e1 = Either.left(new Foo());
Either<Bar, String> e2 = foo.flatMap(x -> doThing2());
Bar bar = e2.left; // Where did this come from???
flatMap() itself would have had to invent a Bar instance somehow. If you start writing a flatMap() that can change both types, you'll see the issue more clearly:
public <C, D> Either<C, D> flatMap(Function<B, Either<C, D>> f) {
if (this.isRight()) {
return f.apply(this.right);
} else {
// Error: can't convert A to C
return Either.left(this.left);
}
}
You can, but your old Left has to be a subtype of or equal to the new Left, so it can be cast up. I'm not very familiar with Java's syntax, but the Scala implementation looks like:
def flatMap[A1 >: A, B1](f: B => Either[A1, B1]): Either[A1, B1] = this match {
case Right(b) => f(b)
case _ => this.asInstanceOf[Either[A1, B1]]
}
Here the A1 >: A designates A as a subtype of A1. I know Java has an <A extends A1> syntax, but I'm not sure it can be used to describe the constraint on A1, as we need in this case.
Regarding your usage of Either (doThing(...)) it seems your flat mapping isn't sufficient. I assume you want your flat mapping work like for Optional<T>.
The mapper ofOptional.flatMap takes a value of kind of T and returns an Optional<U> where U is a generic type parameter of this method. But Optional has one generic type parameter T whereas Either has two: A and B. So if you want to flat map an Either<A,B> either it isn't sufficient to use one mapping.
One mapping what should it map? "The value which isn't null" you would say - wouldn't you? Ok but you know that first at runtime. Your flatMap method is defined at compile time. Therefore you have to provide a mapping for each case.
You choose <C> Either<A, C> flatMap(Function<B, Either<A, C>> f). This mapping uses a value of type B as input. That means if the mapped Either either is !either.isRight() all following mappings would return an Either.left(a) where a is the value of the very first Either.left(a). So actually only an Either either where either.isRight() could be mapped to another value. And it has to be either.isRight() from the beginning. This means also that once an Either<A,B> either is created all flat mappings will result in a kind of Either<A,?>. So the current flatMap restricts an Either either to keep its left generic type. Is this what you supposed to do?
If you want to flat map an Either either without restrictions you need mappings for both cases: either.isRight() and !either.isRight(). This will allow you to continue the flat mapping in both directions.
I did it this way:
public class Either<A, B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public boolean isRight() {
return right != null;
}
#Override
public String toString() {
return isRight() ?
right.toString() :
left.toString();
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C, D> Either<C, D> flatMap(Function<A, Either<C, D>> toLeft, Function<B, Either<C, D>> toRight) {
if (this.isRight()) {
return toRight.apply(this.right);
} else {
return toLeft.apply(this.left);
}
}
public static void main(String[] args) {
Either<String, String> left = Either.left(new Foo("left"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"));
System.out.println(left); // left.right.right
Either<String, String> right = Either.right(new Foo("right"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"))
.flatMap(l -> Either.right(l.toString() + ".right"), r -> Either.left(r.toString() + ".left"));
System.out.println(right); // right.left.left.right
}
private static class Foo {
private String s;
public Foo(String s) {
this.s = s;
}
#Override
public String toString() {
return s;
}
}
private static class Bar {
private String s;
public Bar(String s) {
this.s = s;
}
#Override
public String toString() {
return s;
}
}
private static class Baz {
private String s;
public Baz(String s) {
this.s = s;
}
#Override
public String toString() {
return s;
}
}
}
Answering your question: Yes it is possible to construct an Either returning a different left value. But I think your intent was to know how to get a proper working Either.
I have a collection of objects that I wish to convert freely in-between. Let us call them A through F.
The relationship between the objects might look something like this
A -- B -- C -- F
| |
D -- E ----
What this means is A can be converted to B but not C, if you want to convert A to C then you have to convert it to B first, then to C from there.
I'm having trouble coming up with an extensible and flexible implementation of this that would allow the easy addition of new types and the conversions that go with them. Even after combing through every design pattern I could find I'm no closer to coming up with an answer.
Initially, I had one Converter class that looked something like this
public class Converter
public B convA2B(A in){...}
public A convB2A(B in){...}
public C convB2C(B in){...} etc
this proved to be unwieldy as I tried to add more types. Next, I tried having multiple converter objects all extending the same abstract class
public abstract class Converter
final static int typeA = 0;
final static int typeB = 1;
final static int typeC = 2;
int type;
public abstract A toA(Object in);
public abstract B toB(Object in);
public abstract Object fromA(A in);
public abstract Object fromB(B in);
...
public Object convertTo(int type, Object data)
{
switch(type){
case 0: return new A().toA(data)
etc etc
Essentially what would happen is each converter would convert the data to the next object type in the path, before passing that data on to the next converter.
ie, if I wanted to convert from A to C, A.toC(x) would call B.toC(A.toB(x)).
this didn't work because each converter type needed to have some basic understanding of the relationship between all the types in order to know when converter to call next, which meant adding new converters became quite difficult in places and could even lead to infinite loops if handled poorly.
What should I do? Many of the design patterns I read about seem to be close to what I'm looking for, like mediator, chain of responsibility, interpreter, but I'm not certain how to adapt them to do what I want.
Interesting problem. This is what I came up with:
A base abstract Model class from which A, B, C... will extend:
public abstract class Model {
protected Set<Class<? extends Model>> targets;
public abstract Set<Class<? extends Model>> targets ();
public abstract <T extends Model> T convert (Class<T> target);
}
The B class for example (since it has the most connections)
public class B extends Model {
public B () {
}
#Override
public Set<Class<? extends Model>> targets () {
if (targets == null) {
targets = new HashSet<> ();
targets.add (A.class);
targets.add (C.class);
targets.add (D.class);
}
return targets;
}
#SuppressWarnings ("unchecked")
#Override
public <T extends Model> T convert (Class<T> target) {
if (target == A.class) {
return (T)toA ();
}
if (target == C.class) {
return (T)toC ();
}
return (T)toD ();
}
private A toA () {
A a = new A ();
// your conversion code
return a;
}
private C toC () {
C c = new C ();
// your conversion code
return c;
}
private D toD () {
D d = new D ();
// your conversion code
return d;
}
}
And your converter class:
public class Converter {
public Converter () {
}
public <S extends Model, T extends Model> T run (S source, Class<T> target) throws Exception {
if (!source.targets ().contains (target)) {
throw new Exception ("Inconvertible types.");
}
return source.convert (target);
}
}
And finally on your code, what you'll do is:
B b = new B ();
Converter converter = new Converter ();
try {
C c = converter.run (b, C.class);
F f = converter.run (c, F.class);
E e = converter.run (f, E.class);
} catch (Exception e) {
e.printStackTrace ();
}
This solution is the same thing as your first idea, but it neatly sidesteps the unwieldiness of the combinatorial explosion by treating Converters as objects that can be combined.
Define interface Converter<A, B> as a converter from A to B. This is going to end up the same as Function<A, B>, but we are attaching some extra semantics to it, so we may as well not confuse them:
#FunctionalInterface
interface Converter<A, B> {
B convert(A a);
}
Define a bunch of Converters. For each class, only define Converters for their direct neighbors (Converter<A, B>, but no Converter<A, C>).
Converter<A, B> aToB = a -> { ... };
Converter<B, C> bToC = b -> { ... };
Converter<B, D> bToD = b -> { ... };
// etc.
Now, to get from A to D, we need to combine Converters somehow. This is easily done by adding to Converter:
#FunctionalInterface
interface Converter<A, B> {
B convert(A a);
default <C> Converter<C, B> after(Converter<? super C, ? extends A> pre) {
return c -> this.convert(pre.convert(c));
}
default <C> Converter<A, C> then(Converter<? super B, ? extends C> post) {
return a -> post.convert(this.convert(c));
}
}
Now, you can write
// For a mathematician
Converter<A, C> aToC = bToC.after(aToB);
// If you aren't a mathematician
Converter<A, C> aToC = aToB.then(bToC);
You shouldn't store these compositions in a static somewhere, because then you'll get a hard-to-manage combinatorial explosion, one for every path in your graph. Instead, the code simply creates them as-needed with after and then. Adding a new type involves adding new Converters for its immediate neighbors in the graph.
If you don't feel like using Converter, you can use java.util.function.Function, which is the same thing (but convert is apply, after is compose, and then is andThen). The reason I wrote a new interface is that Converter is semantically different from Function. A Converter is a part of your specific universe of types; a Function is a function between any two types. Although they have the same code, they mean different things (you could say that Converter<A, B> extends Function<A, B> though).
I have 3 files somewhat like so:
public class AConfig {
BConfig bConfig; //assume initialized
private B getA() {
return bConfig.getB(this::pickWhich);
}
public <T> Wrapper<T> pickWhich(T a, T b) {
//logic to decide whether to return a or b that doesn't actually use a or b
//store a or b in c
return new Wrapper<>(c);
}
}
.
public class BConfig {
CConfig cConfig; //assume initialized
B b1, b2; //assume initialized
public <T> B getB(BiFunction<T, T, Wrapper<T>> function) {
C c = cConfig.getC(function);
return createB(function.apply(b1, b2), c);
}
private B createB(Wrapper<B> b, C c) {
//...
}
}
.
public class CConfig {
C c1, c2; //assume initialized
public <T> C getC(BiFunction<T, T, Wrapper<T>> function) {
return createC(function.apply(c1, c2));
}
public C createC(Wrapper<C> c) {
//...
}
}
What I'm trying to do is make it so I can pass down the function and use it in both methods getB and getC, but I get errors in the apply parenthesis saying "apply (T, T) in BiFunction cannot be applied to (B, B)" and the same error in getC but with "(C, C)".
I know if I change getB to
public B getB(BiFunction<B, B, Wrapper<B>> function) {
C c = cConfig.getC(function);
return createB(function.apply(b1, b2), c);
}
It will work fine, but then I can't pass the function down through the getC call.
Is there any way to keep the function generic so it can be used in both methods?
The problem is that createB requires as an input a Wrapper<B> but the only thing that is guaranteed in the current code to have is a Wrapper<T> where T is totally unbound (can be anything).
Same with createC it would need that the input BiFunction returns a Wrapper<C>.
So the problem is whether you can construct a BiFunction that would simultaneously have a Wrapper and Wrapper return.
If T extends B and C (so is declared as <T extends B & C> This seems to
be accomplished.
However since you intent to apply the BiFunction on B and C arguments T also need to be a supper of both B and C .... so in the end T, B and C need to be the same class!!!
I think you may need to rethink your design.
If I create a Functional interface:
#FunctionalInterface
public class Consumer2<T1, T2> {
void accept(T1 t1, T2 t2);
default Consumer1<T2> curry(T1 t1) {
return (t2) -> accept(t1, t2);
}
}
Now, if I have a class:
public class MyClass {
public void printStrings(String a, String b) {
System.out.println(a + ": " + b);
}
}
MyClass myClass = new MyClass();
Now, if I want to use my functional interface, I can:
Consumer2<String, String> printString = myClass::printStrings;
printString.curry("hello").accept("world");
But I can't do something like:
myClass::printStrings.curry("hello").accept("world");
which makes sense, because Java has no way of knowing that myClass::printStrings can be applied to the functional interface Consumer2. To do this, I have created a utility class:
public class F {
public static <T1, T2> Consumer2<T1, T2> c2(Consumer2<T1, T2> fn) {
return fn;
}
}
Then I can:
F.c2(myClass::printStrings).curry("hello").accept("world");
Even, this will work:
((Consumer2<String, String>)myClass::printStrings).curry("hello").accept("world");
As long as there is some way for Java 8 to understand that functional type in this case. So, the question is, what is the best way to do it, while possibly avoiding the boilerplate?
You are not currying but performing partial function application. These operations are related, but not identical. Currying means to transform your Consumer2<T1, T2> to a Function<T1,Consumer1<T2>>. When applying that curried function to a T1 value you get what your method is effectively doing.
It’s easier to use the established name bind as binding a value to a function’s parameter is something, every developer understands without needing to dive deeply into the world of functional programming.
That said, it’s best to remember that now interfaces can have static methods, so there is no need for such utility classes. Further, a static method just returning its argument is of little use on its own, so you may fuse it with the follow-up method that it is supposed to serve. Then, it’s fulfilling the same purpose as the instance method and can be offered as a simple overload:
#FunctionalInterface
public interface Consumer2<T1, T2> {
void accept(T1 t1, T2 t2);
default Consumer1<T2> bind(T1 t1) {
return bind(this, t1);
}
static <T,U> Consumer1<U> bind(Consumer2<? super T, ? super U> c, T t) {
return u -> c.accept(t, u);
}
}
public interface Consumer1<T1> extends Consumer<T1> {}
public class MyClass {
public static void printStrings(String a, String b) {
System.out.println(a + ": " + b);
}
public static void main(String[] args) {
Consumer2.bind(MyClass::printStrings, "hello").accept("world");
}
}
On the other hand, when you use the existing standard interfaces Consumer and BiConsumer you have no choice but to offer a utility method in a class different to these interfaces. But the good news is that it’s easy to make the solution consistent then, as you can’t provide anything else but a static method:
class FunctionUtil {
static <T,U> Consumer<U> bind(BiConsumer<? super T, ? super U> c, T t) {
return u -> c.accept(t, u);
}
}
public class MyClass {
public static void printStrings(String a, String b) {
System.out.println(a + ": " + b);
}
public static void main(String[] args) {
FunctionUtil.bind(MyClass::printStrings, "hello").accept("world");
}
}
Your solution involving F.c2 method is interesting, but your example is too artificial. If you ask, how to write better this code
F.c2(myClass::printStrings).curry("hello").accept("world");
Then I would definitely advise you to write like this:
myClass.printStrings("hello", "world");
If you want to ask how to bind the predefined parameters to the method reference, I would advise you to use lambda function instead:
Consumer1<String> fn = str -> myClass.printStrings("hello", str);
fn.accept("world");
Probably you want to consider the case when your function is not known at the compile time. In this case it's either returned from another method or passed to current method as method parameter or stored in the variable/field. In all of these cases it's already the functional interface and you can use the curry directly:
Consumer2<String, String> getConsumer2() { return myClass::printStrings; }
getConsumer2().curry("hello").accept("world");
Thus in general I don't see the problem here. If you still think that applying currying to the method reference is useful, I would create a static curry method (though I guess, it's actually a "bind", not "curry") in the Consumer1 interface:
static <T1, T2> Consumer1<T2> curry(Consumer2<T1, T2> c2, T1 t1) {
return c2.curry(t1);
}
And use it like this:
Consumer1.curry(myClass::printStrings, "hello").accept("world");
Suppose I have the following hierarchy of classes:
public class MainClass {
}
class A extends MainClass {
}
class B extends MainClass {
}
class C extends MainClass {
}
Now suppose I have a List<MainClass> classes which looks like:
{A, MainClass, A, B, B, MainClass, C, A, C, B, A}
I want to be able to pick out sublists of objects by their class. For example, I would like to be able to extract only those classes in this list of class A (but not class MainClass). As such, using isAssignableFrom(A.class) will not work for me.
My current method looks like:
public <T extends MainClass> List<T> getClasses(List<MainClass> classes, Class classToCollect) {
List<T> subclasses = new ArrayList<T>();
for (MainClass clazz : classes) {
if (clazz.getClass().isInstance(classToCollect)) {
subclasses.add((T)clazz);
}
}
return subclasses;
}
This still doesn't work and passes back an empty list. What gives here?
The condition should look like this:
for (MainClass obj : classes) {
if (classToCollect.isInstance(obj)) {
subclasses.add((T)obj);
}
}
The name clazz is misleading, because it is actually an object.
You can further improve type safety of your code by using Class<T> in the method header:
public <T extends MainClass> List<T> getClasses(List<MainClass> classes, Class<T> classToCollect) {
...
}
Demo on ideone.
Note: This would not work if you pass MainClass.class as the second argument (Thanks, JB Nizet, for a great comment).
Using Java 8 you could express it this way:
public <T> List<T> getClasses(List<? super T> instances, Class<T> classToCollect) {
return instances.stream()
.filter(c -> c.getClass() == classToCollect)
.map(c -> (T) c)
.collect(Collectors.toList());
}