Jacob Wang
Jacob Wang

Reputation: 4794

Scala - Abstract type in constructors

Consider the following code:

sealed trait SpecieType
case class Mammal() extends SpecieType
case class Insect() extends SpecieType

abstract class Specie(name: String, specie: T) {
  type T <: SpecieType
}

However this fails to compile (line 5) because the abstract type is defined inside Specie class definition.

What do I need to change to make this work?

The reason why I needed an abstract type is because I need the type of its parameter at compile time (to perform implicit lookups for typeclass pattern). Basically, I need to do something like this: implicitly[SomeTypeClass[Specie#T]], where SomeTypeClass is a typeclass that all subclass of SpecieType implements.

Upvotes: 0

Views: 132

Answers (1)

sjrd
sjrd

Reputation: 22085

Unfortunately, this is one of the cases where the class body as constructor creates severe limitations. What you're trying to achieve is impossible per se.

To the best of my knowledge, there are two main alternatives. One is, as suggested by @cchantep in a comment, to use a type parameter instead of an abstract type member. The caveat is that it changes the declaration of Specie, which might make your life tedious in the rest of the program.

The other alternative allows you to retain the type member version at the expense of a bit of unsafety. This might be acceptable because you have an abstract class anyway, so only subclasses need to be careful:

sealed abstract class Specie(name: String, _specie: SpecieType) {
  type T <: SpecieType

  val specie: T = _specie.asInstanceOf[T]
}

class MammalSpecie extends Specie("Mammal", Mammal()) {
  type T = Mammal
}

You can re-enforce safety if you can bear to uglify the definition of subclasses of Specie, using an intermediate abstract class:

sealed abstract class Specie(name: String, _specie: SpecieType) {
  type T <: SpecieType

  val specie: T = _specie.asInstanceOf[T]
}

abstract class SpecieBase[U <: SpecieType](name: String, _specie: U)
    extends Specie(name, _specie) {
  type T = U
}

class MammalSpecie extends SpecieBase("Mammal", Mammal())

Since Specie itself is sealed, the only way to extend from Specie is to actually extend from SpecieBase, which is not sealed. SpecieBase enforces the safety that Specie cannot enforce itself.

Upvotes: 2

Related Questions