Reputation: 5882
I'm running into issues with the following code (heavily simplified). Looks like the problem may be with the way I use abstract type members. I would appreciate someone pointing out and explaining what I'm doing wrong here. Compiler error at the bottom. I'm using Scala version 2.12.
trait DataType {
type A
def convert(value: String): A
def convertToString(value: A): String
}
case object IntType extends DataType {
type A = Int
def convert(value: String): A = value.toInt
def convertToString(value: A): String = value.toString
}
trait Codec[T <: DataType] {
val dtype: T
def encode(data: Array[String]): Array[T#A]
def decode(data: Array[T#A]): Array[String]
}
class CodecImp[T <: DataType](val dtype: T)(implicit tag: ClassTag[T#A]) extends Codec[T] {
def encode(data: Array[String]): Array[T#A] = {
Array[T#A](dtype.convert(data(0)))
}
def decode(data: Array[T#A]): Array[String] = {
Array[String](dtype.convertToString(data(0)))
}
}
val cod = new CodecImp(IntType)
val encoded = cod.encode(Array("1", "2", "3")) // expecting: Array[IntType.A]
val decoded = cod.decode(encoded) // expecting: Array[String]
Compiler error.
Error:(30, 50) type mismatch;
found : T#A
required: CodecImp.this.dtype.A
Array[String](dtype.convertToString(data(0)))
^
Upvotes: 0
Views: 1301
Reputation: 111
I found What does the #
operator mean in Scala? explained the '#' operator pretty well.
Each instance of DataType has it's own path dependent type A.
The difference between: T#A
meaning A is a nested class of any T and dtype.A
meaning the A class of dtype
You could change the Codec trait method signatures to something like:
def encode(data: Array[String]): Array[dtype.A]
def decode(data: Array[dtype.A]): Array[String]
But type parameters might be a better way to express the relation.
Upvotes: 2
Reputation: 14217
This is caused by dtype.convertToString(data(0))
this method is wanting a variable with type: dtype#A
, this type is decided by the variable dtype
, but data
type is Array[T#A]
, so this caused type mismatch, it's can't resolve in Scala, Since we can't state our method, like: def decode(data: Array[dType.A])...
that's compiler expected.
And You can solve this by generics type agains type alias, like:
trait DataType[T] {
def convert(value: String): T
def convertToString(value: T): String
}
case object IntType extends DataType[Int] {
def convert(value: String): Int = value.toInt
def convertToString(value: Int): String = value.toString
}
class CodecImp[B](val dtype: DataType[B])(implicit tag: ClassTag[B]) {
def encode(data: Array[String]): Array[B] = {
Array[B](dtype.convert(data(0)))
}
def decode(data: Array[B]): Array[String] = {
Array[String](dtype.convertToString(data(0)))
}
}
Upvotes: 1
Reputation: 40500
The thing is that each instance of DataType
can have A
defined as anything whatsoever. Consider this:
class Foo extends DataType {
type A = Int
def convertToString(i: Int) = i.toString
def convert(s: String) = s.toInt
}
class Bar extends DataType {
type A = String
def convertToString(s: String) = s
def convert(s: String) = s
}
Now, if I do val codecs = Seq(CodecImpl(new Foo, new Bar)
and if that compiles, then what type should the argument be to codecs.map(_.decode(whateverMakesSenseHere))
?
This doesn't work ... You can't use T#A
like this, because it is abstract.
I get a feeling, you'd be better off modeling what you need to model with type parameters rather than path dependent types. This just doesn't really seem like a use case for the latter.
Upvotes: 0