-1

I have the following where I set information and extractors for different schemes of data:

trait DataScheme {
    type Type <: List[Any]
    class ExtractorMethods(ticker: String, dataList: List[Type]) {
        def getDatetime(datum: Type): Date = new Date(datum(columnIndex(Names.datetime)).toString)
        def upperDatum(date: Date): Type = dataList.minBy(datum => getDatetime(datum) >= date)
        def lowerDatum(date: Date): Type = dataList.maxBy(datum => getDatetime(datum) <= date)
    }
}
trait IndexScheme extends DataScheme {
    type Type = (Date, Double, Double, Double, Double, Long)
    class ExtractorMethods(ticker: String, dataList: List[Type]) extends super.ExtractorMethods(ticker: String, dataList: List[Type]){
        def testing12(int: Int):Int = 12
        val test123 = 123
    }
}

I want anything extending DataScheme to use its ExtractorMethods methods (e.g. lowerDatum) but also have its own methods (e.g. testing12).

There is a class definition for lists of data elements:

class Data[+T <: DataScheme](val ticker: String, val dataList: List[T#Type], val isSorted: Boolean)
    (implicit m: Manifest[T], mm: Manifest[T#Type]) extends Symbols {
    def this(ticker: String, dataList: List[T#Type])(implicit m: Manifest[T], mm: Manifest[T#Type]) = this(ticker, dataList, false)(m: Manifest[T], mm: Manifest[T#Type])
    val dataScheme: T
    val extractorMethods = new dataScheme.ExtractorMethods(ticker, dataList.asInstanceOf[List[dataScheme.Type]])
}

A Data class should make accessible the methods in ExtractorMethods of the scheme so they can be used in the main program through the instance of Data that has been defined. For example if sortedData is an instance of Data[IndexScheme], the following works:

val lowerDatum = sortedData.extractorMethods.lowerDatum(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2010-03-31 00:00:00"))

but this does not:

val testing = sortedData.extractorMethods.testing12(123)

because 'testing 123 is not a member of sortedData.dataScheme.extractorMethods'. So my question is how can the subclasses of ExtractorMethods in the subtraits of DataScheme like IndexScheme be made accessible? How is it possible using Manifests and TypeTags? Thanks.

  • ExtractorMethods is not a great name for that class. It ls likely to be a source of initial confusion to anybody who has to read your code, given that **extractor** is such a well known term in Scala. – itsbruce Sep 05 '13 at 11:23
  • 1
    BTW, how does this even compile? You shouldn't be able to override `type Type <: List[Any]` with `type Type = (Date, Double, Double, Double, Double, Long)`. Tuples are not a subtype of `List[Any]`. What version of Scala are you using? – itsbruce Sep 05 '13 at 14:39
  • Furthermore, how does the Data class compile when it seems to be abstract? Although you don't have the abstract keyword there. The **dataScheme** val seems not to be instantiated, which should force the class to be abstract. Have you actually compiled this code? Does the **dataScheme** val have any purpose other than to provide the right version of the ExtractorMethods method? – itsbruce Sep 05 '13 at 15:26
  • I have not tried compiling it. You are right, the `<: List[Any]` on Type is wrong. At one point it was actually defined as a list and this has since changed. dataScheme is used to create an instance of ExtractorMethods and also is a window to access other things in DataScheme. How about `val dataScheme = m.erasure.newInstance.asInstanceOf[T]`? – user2748572 Sep 05 '13 at 18:45
  • Update added. But really, this is not the right way to do this. – itsbruce Sep 08 '13 at 00:42
  • You're going to have some issues creating methods for the Data class. http://stackoverflow.com/questions/18679871/scala-anonymous-function-genric-variance-issues – itsbruce Sep 09 '13 at 21:22

1 Answers1

0

So you want the generic class Data[DataScheme] or Data[IndexScheme] to have access to the methods of whichever type Data has been parameterised with. You've tried to do this several different ways, from the evidence in your code.

To answer your last question - manifests can't help in this particular case and TypeTags are only part of the answer. If you really want to do this, you do it with mirrors.

However, you will have to make some changes to your code. Scala only has instance methods; there are no such things as static methods in Scala. This means that you can only use reflection to invoke a method on an instance of a class, trait or object. Your traits are abstract and can't be instantiated.

I can't really tell you how to clean up your code, because what you have pasted up here is a bit of a mess and is full of different things you have tried. What I can show you is how to do it with a simpler set of classes:

import scala.reflect.runtime.universe._

class t1 {
  class Methods {
    def a = "a"
    def b = "b"
  }
  def methods = new Methods
}

class t2 extends t1 {
  class Methods extends super.Methods {
    def one = 1
    def two = 2
  }
  override def methods = new Methods
}

class c[+T <: t1](implicit tag: TypeTag[T]) {

  def generateT = {
    val mirror = runtimeMirror(getClass.getClassLoader)
    val cMirror = mirror.reflectClass(typeOf[T].typeSymbol.asClass)
    cMirror.reflectConstructor(typeOf[T].declaration(nme.CONSTRUCTOR).asMethod)
  }
  val t = generateT().asInstanceOf[T]
}

val v1 = new c[t1]
val v2 = new c[t2]

If you run that, you'll find that v1.t.methods gives you a class with only methods a and b, but v2.t.methods gives a class with methods one and two as well.

This really is not how to do this - reaching for reflection for this kind of job shows a very broken model. But I guess that's your business.

I stick by what I said below, though. You should be using implicit conversions (and possibly implicit parameters) with companion objects. Use Scala's type system the way it's designed - you are fighting it all the way.

ORIGINAL ANSWER

Well, I'm going to start by saying that I would never do things the way you are doing this; it seems horribly over-complicated. But you can do what you want to do, roughly the way you are doing it, by

  1. Using mixins
  2. Moving the extractorMethods creation code into the traits.

Here's a greatly simplified example:

trait t1 {
  class Methods {
    def a = "a"
    def b = "b"
  }
  def methods = new Methods
}

trait t2 extends t1 {
  class Methods extends super.Methods {
    def one = 1
    def two = 2
  }
  override def methods = new Methods
}

class c1 extends t1 
val v1 = new c1
// v1.methods.a will return "a", but v1.methods.one does not exist
class c2 extends c1 with t2
val v2 = new c2
// v2.methods.a returns "a" and v2.methods.one returns 1

I could replicate your modus operandi more closely by defining c1 like this:

class c1 extends t1 {
  val myMethods = methods
}

in which case v1.myMethods would only have methods a and b but v2.myMethods would have a, b, one and two.

You should be able to see how you can adapt this to your own class and trait structure. I know my example doesn't have any of your complex type logic in it, but you know better than I what you are trying to achieve there. I'm just trying to demonstrate a simple mechanism.

But dude, way to make your life difficult...

EDIT

There are so many things I could say about what is wrong with your approach here, both on the small and large scale. I'm going to restrict myself to saying two things:

  1. You can't do what you are trying to do in the Data class because it is abstract. You cannot force Scala to magically replace an uninitialised, abstract method of a non-specific type with the specific type, just by littering everything with Type annotations. You can only solve this with a concrete class which provides the specific type.

  2. You should be doing this with implicit conversions. Implicits would help you do it the wrong way you seem fixated on, but would also help you do it the right way. Oh, and use a companion object, either for the implicits or to hold a factory (or bot).

itsbruce
  • 4,825
  • 26
  • 35
  • First, there should be only the need for the Data[+T <: DataScheme] class, that is why I put a type on it. Not Data1 extends t1, Data2 extends t2, etc. I want simplicity too; but I want to make the code abstract and not have to duplicate code over and over. Second, I am instantiating class ExtractorMethods with parameters, so this cannot be done in the trait. Yes I can use your idea of creating class Data1 extends t1 and instantiating an ExtractorMethods in it, maybe separate from the trait altogether, but again I want to avoid duplicating the class over and over. So is it possible? – user2748572 Sep 05 '13 at 14:04
  • I can' answer your question specifically until you provide some further answers (see the comments added to the original question above). Your code doesn't even look as if it should compile. It is also very, very smelly (in the bad code smells sense). In general, I would say the best way to get the level of abstraction you want is to use a factory (maybe hidden in a companion object) to generate the classe – itsbruce Sep 05 '13 at 15:38
  • Oh, and "I am instantiating class ExtractorMethods with parameters, so this cannot be done in the trait" makes no sense. Of course it can be done. – itsbruce Sep 05 '13 at 15:45
  • Yes, the code looks like it could be better to me as well, that is why I am here. As soon as I am forced to use asInstanceOf the first thing I think of is "I'm wrong". I think I have backed myself into a corner with trying to eliminate repetition of code. Wouldn't a factory still require constructing multiple subclasses of `Data`? Also I mean I have `val extractorMethods = new dataScheme.ExtractorMethods(ticker, dataList.asInstanceOf[List[dataScheme.Type]])` not `val extractorMethods = new dataScheme.ExtractorMethods()` so why should `ticker, dataList` be provided again when Data has it. – user2748572 Sep 05 '13 at 18:53