2

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 ?

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
Jean
  • 10,545
  • 2
  • 31
  • 31

3 Answers3

4

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) {
  }
}
psp
  • 12,138
  • 1
  • 41
  • 51
  • 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 Oct 13 '10 at 07:03
  • The REPL wraps your code in an invicible object, so you'll have to surround that code in "object Foo { }" if you want to run it in the REPL. – Viktor Klang Oct 13 '10 at 08:06
2

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
  • 1,036
  • 1
  • 10
  • 16
  • Sorry if it wasn't clear, by Callable I meant java.util.concurrent.Callable[T], which defines call: () => T. – Jean Oct 13 '10 at 04:20
  • 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 Oct 13 '10 at 05:51
  • 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 Oct 13 '10 at 06:40
2

Put the class inside the object companion.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 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 Oct 13 '10 at 05:38
  • 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 Oct 13 '10 at 07:05