3
votes

I am trying to attach extra data to other types, and have a trait similar to:

trait ExtraData {
  def getExtraData() : Array[Byte]
}

And I'm currently using it like this:

class ExternalType1WithExtraData(superType:ExternalType1, bytes:Array[Byte]) extends ExternalType1(superType.a,superType.b, ...) with ExtraData {
  def getExtraData() : Array[Byte] = bytes
}

class ExternalType2WithExtraData(superType:ExternalType2, bytes:Array[Byte]) extends ExternalType2(superType.z,superType.w, ...) with ExtraData {
  def getExtraData() : Array[Byte] = bytes
}

It seems like there would be a generic way to create these classes, but I haven't been able to find it yet.

--- Begin Edit -- Adding desired behavior

Given a function

def sendData(ex : ExternalType1)

I want to be able to pass my enhanced types to that function.

val data:ExternalType1 = ???
val moredata:ExternalType1 = { new ExternalType1 with ExtraData{...} }
sendData(moredata)

--- End Edit

I've tried to do things along these lines but have had no success:

// Compiler wont let me extend T
class WithExtraData[T](bytes:Array[Byte]) extends T with ExtraData{
  def getExtraData() : Array[Byte] = bytes
}

:12: error: class type required but T found class WithExtraDataT extends T with ExtraData{ ^ :12: error: illegal inheritance; supertype T is not a subclass of the superclass Object of the mixin trait ExtraData class WithExtraDataT extends T with ExtraData{

// Seems closer, but doesn't work.
class WithExtraData[T](t:T, bytes:Array[Byte]) extends ExtraData {
  this : T => t
  def getExtraData() : Array[Byte] = bytes
}

:13: warning: a pure expression does nothing in statement position; multiline expressions may require enclosing parentheses self : T => t ^ defined class WithExtraData

scala> new WithExtraData[String]("hi", new ArrayByte) :13: error: class WithExtraData cannot be instantiated because it does not conform to its self-type WithExtraData[String] with String

Is there a way to achieve this?

2
I am guessing that you are omitting an important requirement. Are you trying to extend ExternalType1 so that you can pass this instance to some external library, get it back, cast to ExtraData and then getExtraData? If not, you should definitely stay away from subclassing.drstevens
Yes, I'm trying to do something quite similar to that, but to Kryo with custom serializers.Austin Harris

2 Answers

2
votes

I think that the closest you can reasonably get (at least without macros) is not to extend ExternalType1, but to have an implicit conversion instead:

class WithExtraData[T](val value: T, bytes: Array[Byte]) extends ExtraData {
  def getExtraData(): Array[Byte] = bytes
}

object WithExtraData {
  implicit def getValue[T](x: WithExtraData[T]): T = x.value
}

Then you can e.g. pass WithExtraData[ExternalType1] whenever ExternalType1 is required.

0
votes

You basically try to enrich your types with additional type information represented by values if I understand you correct? In that case, the concept you looking for is called Dependent Types

Scala does not support this concept natively (like for example Idris). However, there are some workarounds to get closer.

One example would be to use Shapeless

import shapeless._
import SingletonTypes._

val test1: ^((20 + 50) > 1) = true
test1: Boolean(true) = true

val test2: ^((20 + 50) > 1) = false
<console>:11: error: type mismatch;
 found   : Boolean(false)
 required: Boolean(true)