1

I have the following snippet of Java code using Vavr. Type-checking fails unless I inline a parameter.

Why can the code below not be accepted by the compiler?

import io.vavr.Function1;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import io.vavr.collection.List;
import io.vavr.Option;

import static io.vavr.collection.List.unfoldRight;

class A {}
class B {}
class Main {
    Function1<A, Option<Tuple2<B, A>>> f = (a) -> Option.of(Tuple.of(new B(), new A()));
    List<B> L0 = unfoldRight(new A(), f); // *
    List<B> L1 = unfoldRight(new A(), (a) -> Option.of(Tuple.of(new B(), new A()));

    Option<Tuple2<B, A>> g(A a) { return Option.of(Tuple.of(new B(), new A())); }
    List<B> L2 = unfoldRight(new A(), (a) -> g(a)); // **
}


// *  Compilation fails with: "Incompatible equality constraint: ? extends T and A"

// ** Compilation fails with: "Incompatible equality constraint: ? extends A and A"

Here's the method signature for unfoldRight from the Vavr library:

static <T, U> List<U> unfoldRight(T seed, Function<? super T, Option<Tuple2<? extends U, ? extends T>>> f)

and here's a link to the Github documentation for same:

https://github.com/vavr-io/vavr/blob/master/vavr/src/main/java/io/vavr/collection/List.java#L644-L671

Zaaier
  • 685
  • 8
  • 22

1 Answers1

3

The key is that Option<Tuple<A, B>> isn't an instance of Option<Tuple<? extends A, ? extends B>> (although it is a Option<? extends Tuple<? extends A, ? extends B>>).

Consider the case of a List<Map<A, B>> and List<Map<? extends A, ? extends B>> (which is the same from a type safety perspective as your code). If you could write:

List<Map<A, B>> list = new ArrayList<>();

// Compiler error! Pretend it's OK, though.
List<Map<? extends A, ? extends B>> list2 = list;

Map<SubclassOfA, SubclassOfB> map = new HashMap<>();
list2.add(map);

list.get(0).put(new A(), new B());

This is now a problem, because map contains a key/value pair of type A,B, not SubclassOfA,SubclassOfB. As such, you'd get a ClassCastException if you tried to get things from map.

// ClassCastException!
SubclassOfA soa = map.keySet().iterator().next();

Ideone demo

Hence, it's disallowed by the compiler.

If list2 were declared as List<? extends Map<? extends A, ? extends B>>, you couldn't call list2.add(map), so you couldn't get the same problem. Hence, that assignment would be allowed.

Add wildcard upper bounds to your types:

Function1<A, Option<Tuple2<? extends B, ? extends A>>> f = ...
Option<Tuple2<? extends B, ? extends A>> g(A a) { ... }
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Oh, right, it's ```Incompatible inequality constraint: (? extends A, A)```, not ```Incompatible inequality constraint: (? extends A, ? extends A)```. Thanks very much! – Zaaier Nov 18 '17 at 03:03