3

I need help with Scala's Generics.

I have the following "abstract" traits:

trait Base[B <: Base[B,M], M <: Meta[B,M]] { def meta: M } // Manages instances of a general type trait Meta[B <: Base[B,M], M <: Meta[B,M]] { // ... } // Manages Metas of a general type trait Manager[M <: Meta[_,M]] { def apply[N <: M](clazz: Class[N]): N } 

Then I wan to define some more specific type hierarchy like this:

trait Thing[B <: Thing[B,M], M <: ThingMeta[B,M]] extends Base[B,M] { // ... } trait ThingMeta[B <: Thing[B,M], M <: ThingMeta[B,M]] extends Meta[B,M]{ // ... } trait ThingManager extends *Manager[ThingMeta[_,_]]* { // ... } 

The last declaration gives me this error:

type arguments [ThingMeta[_, _]] do not conform to trait Manager's type parameter bounds [M <: Meta[_, M]] 

How can I say that ThingManager, is a Manager for all ThingMetas, and therefore does not itself take a type parameter.

1
  • FYI, I want to program this without "statics", so one instance of "Meta" plays about the same role as a Scala "object", and the "Manager" gives access to all "Metas" of some general category of objects. Commented Aug 8, 2011 at 16:25

4 Answers 4

2

You relaxed the declaration of Manager by removing the bound here, but that's too much:

trait Manager[M <: Meta[_,M]] { 

Why don't you just avoid mentioning M recursively? With the following line the code you provided compiles.

trait Manager[M <: Meta[_,_]] { 

This solution is similar to paradigmatic's solution - but I don't see the advantage in using abstract types. The only advantage would be if they allowed you writing things such as:

type M = ThingMeta[_,M] 

in ThingManager.

Sign up to request clarification or add additional context in comments.

2 Comments

+1. It is as good as my solution. I just tried abstract types first and it worked.
Confused I'm sure I tried that as well, but it works, so I must not have. That's exactly what I was looking for.
2

If you compile it with -explaintypes, you'll see this:

ThingMeta[_, _] <: Meta[_, ThingMeta[_, _]]? 

Clearly enough, this is not true. The problem is that Manager simply does not allow you any freedom in the choice of the second type parameter of M: it must be M itself -- which leads to recursion unless you are defining a trait or class.

Maybe Miles Sabin will come up with a way to do it -- he often surprises me with ways around things that I thought impossible -- but, perhaps, it would be better to use abstract types instead of type parameters, or loosen up the boundaries.

2 Comments

Currently, I use "trait Manager[M]" instead of "trait Manager[M <: Meta[_,M]]" which works, but is somewhat weaker. I usually go with Generics because I come from Java, and that is more "natural" for me. Is there a way to force Eclipse to use -explaintypes when compiling?
@Sebastien Curiously enough, there doesn't seem to be a way. I think I'll open a ticket about it. Meanwhile, from REPL you can enter power mode (:power) and then activate it with settings.explaintypes.value = true.
1

A solution more constrained than your work around:

trait Manager { type M <: Meta[_,_] def apply[N <: M](clazz: Class[N]): N } trait ThingManager extends Manager { type M = ThingMeta[_,_] } 

2 Comments

Thank you. So far it's seems the best solution.
But how is it better than your own solution with no constraint at all?
0

What about high order types?

trait Manager[MetaType[B,M] <: Meta[B,M]] { def apply[N <: MetaType[_, _]](clazz: Class[N]): N } trait ThingManager extends Manager[ThingMeta] 

Sorry I have no scala installation available to check this.

Edit. Got one scala installation. Previous code does not compile (constraints on the parameters of Meta in the constraint on MetaType not satisfied). But this code does :

trait Manager[ BaseType[B <: BaseType[B,M], M <: MetaType[B,M]] <: Base[B,M], MetaType[B <: BaseType[B,M], M <: MetaType[B,M]] <: Meta[B,M]] { def apply[N <: MetaType[_,_]](clazz: Class[N]): N } trait ThingManager extends Manager[Thing, ThingMeta] {} 

4 Comments

I see. But is it not so that in this case, B and M don't actually add any information, because they are not used inside the trait? My current work-around is simply to define "trait Manager[M]" instead of "trait Manager[M <: Meta[_,M]]", which seems equivalent to your code.
I was under the (false) impression that your type manager would use the fact that it deals with Meta. If it contains only the apply declaration, better not to mention it at all. The fact that Manager might then be instanciated for types completely unrelated to Meta can be seen as faulty, I think it is not, constraints based on intended usage rather than actual need are a bad idea. I'm surprised by apply signature, class of Meta yields instance of Meta. I would have expected class of Base yields instance of Meta, in my code apply[N](clazz: Class[N]) : MetaType[N, _]
I want to use Meta to replace "object Base". So an instance of Base can get it's Meta (without keeping a permanent reference) but, and that explains the signature, a Meta can also use the Manager to get another Meta, in the same way that one "object" can depend on another.
I'm not sure I grasp the whole picture there. Nevermind, is it only tagentially related to the technical question. If TypeManger is only what you wrote, I think your solution is the better one. Maybe a lower bound could be useful too.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.