views:

102

answers:

3

I would like to parameterize a type with one of its subclasses. Consider the following:

class DataLoader {
  class Data { /* data specifics to this data loader */ }
  def getData : Data /* and so on */
}

Now I want to make this loader able to asynchronously retrieve data from the network. One of the options is to have it subclass Callable.

class DataLoader extends Callable[Data] {
  class Data { /* ... */ }
  def call : Data = { ... }
}
val futureData = executor.submit(new DataLoader)
futureData.get

Scala will not let me do that, because when I supply the parameters of Callable, Data is not known yet. If I write DataLoader.Data, Scala calls me off for a cyclic reference.

Sure, I could write my data class outside of my loader, but there are cases where it's nicer inside. Sure, another alternative would be to have, say, a DataManager, with inside a Data type and a Loader which extends Callable[Data] - which in this case is arguably better design. But those issues aside, is there any way I can actually implement a trait that involves writing a function returning a T, setting T to be an inner class ?

+1  A: 

What about this?

trait Callable {
  type TData
  def getData: TData
}
class DataLoader extends Callable {
  type TData = Data
  class Data
  override def getData: TData = new Data
}

Edit: Sorry, didn't notice you meant the Callable from java.util.concurrent.. Then adding the following might help:

implicit def c2c[T](c: Callable {type TData = T}) = new java.util.concurrent.Callable[T] {
  def call = c.getData
}

Oleg Galako
Sorry if it wasn't clear, by Callable I meant java.util.concurrent.Callable[T], which defines call: () => T.
Jean
It works, thank you very much :) It requires to write the type TData inside the class as DataLoader#Data. I'd be surprised though if there weren't a less contrived solution... it's quite verbose and creates a useless object. But it works, so thank you very much :)
Jean
You can also create the juc.Callable as field inside your class and return it in implicit conversion to prevent multiple new juc.Callable creation.
Oleg Galako
+2  A: 

Put the class inside the object companion.

Daniel
I'm sorry I'm not sure how to write this. If I write : object DataLoader { class Data {} } ; then I can't seem to access it on the definition line of the class. Callable[Data] can't seem to understand what "Data" is referring to, nor can Callable[DataLoader.Data]. Callable[DataLoader#Data] still gives off a cyclic reference error.
Jean
Sorry: This actually works (see extempore answer), but only compiled in a file. I can't get it to work in the REPL, which is not a practical concern. Thank you :)
Jean
+4  A: 

There are a million ways to achieve something reasonable, so it's hard to know how to answer. I don't think you want to get too attached to the idea of parameterizing a supertype on one of the subtype's own inner classes. Even if it works, it won't.

Out of the million ways I picked something at random and it turned out to involve inversion. I did it this way because you said in a comment you couldn't get it to compile in the companion object, and I'm not sure why that would be.

import java.util.concurrent.Callable

class DataLoader extends Callable[DataLoader.Data] {
  def call = new DataLoader.Data(this)
}

object DataLoader {
  private class Data(loader: DataLoader) {
  }
}
extempore
Hmmm... This works and is exactly what I wanted, when put into a file. What I don't understand is, it doesn't work in the REPL, even putting the object before the class, which is why I thought it wasn't.
Jean
The REPL wraps your code in an invicible object, so you'll have to surround that code in "object Foo { <your code> }" if you want to run it in the REPL.
Viktor Klang