Question

In the company I work for we get many benefits from a (Java) pattern that can be summarized as follows:

There are "things" that we can get by their special/smart "ids". Each "thing" knows its id, and ids are "smart" in that you can ask them to give you the "thing" they identify. In a complex ever-growing hierarchy of "things" this eases maintenance and eliminates a lot of code (duplication and otherwise). I am certain that some of you may find this weird and I could go into explaining why this is, but the fact is that it is this way and it was helpful so far and is not a part of the question.

Summary of Java definitions of this is as follows (slightly rewritten):

interface Id<I extends Id<I,T>, T extends Thing<I,T>> {
  T getThing();
  ...
}

interface Thing<I extends Id<I,T>, T extends Thing<I,T>> {
  public I getId();
  ...
}

At times we need to get a "batch" of things given a batch of varying ids (this goes across network, hence batching to speed things up). For the purpose of this description it can be written as:

public Map<Id<?,?>,Thing<?,?>> getManyThings(List<Id<?,?>> idsToGetThingsFor);

(Method argument is simply a multiplicity of ids, it really does not matter whether it is a collection, list or otherwise)

Now, on the other side of the network, this method was implemented in Scala 2.9, with fairly ugly avoidance of Scala generics strictness. We're trying to upgrade to Scala 2.10 and things are no longer compiling and have to figure out a way to deal with this. The trick is that there is a Scala method summarized as follows:

def getOneThing[I <: Id[I,T],T <: Thing[I,T]](id: I): T;

That method goes and checks, processes and distributes the calls appropriately based on many factors - too much to explain here and irrelevant.

Now, it is the getManyThings() method that iterates over ids passed to it and needs to pass them as arguments to getOneThing(). That is where things fail. getManyThings() only knows that ids it gets are Id[,] essentially and Scala complains that [,] is not good enough for [I <: Id[I,T],T <: Thing[I,T]] that getOneThing() requires:

... error: inferred type arguments [Id[_$16,_$17],Nothing] do not conform to method getOneThing's type parameter bounds [I <: Id[I,T],T <: Thing[I,T]]

Note - declaring non-parameterized (non-generic) base classes for Id and Thing does not really help as it just pushes the problem to (too many) other spots. We chose to be very strictly typed to ensure code quality but we do have to have a way to work with "mixed" collections.

Any help? Please!

Was it helpful?

Solution

I assume you have a method like this:

def getManyThings[I <: Id[I, T], T <: Thing[I, T]](ids:List[I]):List[T] = 
  ids map getOneThing

This will give an error because the compiler can not infer that you want to call the getOneThing method with T as the second type parameter.

In order to 'help' the compiler you need to pass them explicitly:

trait Id[I <: Id[I, T], T <: Thing[I, T]]
trait Thing[I <: Id[I, T], T <: Thing[I, T]]

def getOneThing[I <: Id[I, T], T <: Thing[I, T]](id:I):T = ???

def getManyThings[I <: Id[I, T], T <: Thing[I, T]](ids:List[I]):List[T] = 
  ids map ( getOneThing[I, T](_) )
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top