0

As an example here's the boiled down code.

trait DataType {
    type DType
    // more stuff
}

object IntType extends DataType {
    type DType = Int
    // more stuff
}

trait Codec {
    val dtype: DataType
    // more stuff
}

class DenseCodec(val dtype: DataType) extends Codec {
    def get(size: Int) = new Array[dtype.DType](size)
}

val dense = new DenseCodec(IntType)

dense.get(4)

This will not compile:

cannot find class tag for element type DenseCodec.this.dtype.DType def get(size: Int) = new Array[dtype.DType](size) ^

I know this has something to do with Arrays in Scala being special. I could use here something like Vector which will work fine but the project I'm working on is heavy on data processing and I would like to keep things as efficient as possible.

1 Answer 1

2

This will work

  def get(size: Int)(implicit tag: ClassTag[DataType]) = new Array[DataType](size)

Or using an equivalent context bound

  def get[DType: ClassTag](size: Int) = new Array[DType](size)

you could even replace DType with a template T.

Array's constructor actually takes an implicit parameter of type ClassTag and therefore you need to supply one, either implicitly or explicitly.

EDIT: Also worth noting that using dtype.DType will not work because dtype is of type DataType that has no concrete DType but only an abstract one. Funnily enough dtype.type will work just fine because at runtime val dtype: DataType is already an IntType.

EDIT2: If you want to propagate the type, I am afraid that the only way would be to use type classes.

import scala.reflect.ClassTag

object Types {
  trait DataType[A] {
    // more stuff
  }

  object IntType extends DataType[Int]

  object StringType extends DataType[String]

  trait Codec[B] {
    val dtype: DataType[B]
    // more stuff
  }

  class DenseCodec[B](val dtype: DataType[B]) extends Codec[B] {
    def get(size: Int)(implicit ct: ClassTag[B]) = new Array[B](size)
  }

  def main(args: Array[String]): Unit = {
    val denseInt = new DenseCodec(IntType)
    val arrInt = denseInt.get(4) //Array[Int]

    val denseString = new DenseCodec(StringType)
    val arrString = denseString.get(4) //Array[String]
  }
}

This way you can get rid of the DType and use the type class's type instead. You don't introduce any boilerplate because you wanted to define IntType and probably more types anyway. Hope this one solves your problem :)

EDIT3: If you want to keep your DType just go with

trait DataType[A] {
  type DType = A
  // more stuff
}

and modify get definition

def get(size: Int)(implicit ct: ClassTag[B]) = new Array[dtype.DType](size)

But this way you will have a long type signature derived from IntType, StringType etc..

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

5 Comments

dense.get(4) returns Array[Nothing]. It will work when I do dense[IntType.DType].get(4) but that looks superfluous.
Of course it is - DType is just a type alias and in this case is defined in each class that you define e.g. IntType. You don't introduce a type hierarchy yourself. IntType.DType is just a fancy way of saying Int at compile time.
Updated answer. @marcin_koss
Thanks, this works. I read somewhere type members should be preferred but I guess it always depends on the problem at hand.
Yeah, it is tough. Updated it even further :D I suggest reading up Cats or Scalaz sources to get a hang of it. @marcin_koss

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.