Mikaël Mayer
Mikaël Mayer

Reputation: 10701

Scala: how to add type-dependent methods in trait?

I have the following idea:

trait Generator[A] {
  def generate: Stream[A]

  // (1) If A <: Int
  def +(other: Generator[Int]): Generator[Int] = (
   CustomGeneratorInt(
     (this.asInstanceOf[Generator[Int]].generate, other.generate)
     .zipped
     .map(_ + _))
  )

  // (2) If A <: Boolean
  def &&(other: Generator[Boolean]): Generator[Boolean] = ...
}

case class CustomGeneratorInt(val generate: Stream[Int]) extends Generator[Int]
case class ConstantInt(i: Int) extends Generator[Int] { def generate = Stream(i) }
case class ConstantBool(i: Boolean) extends Generator[Boolean] { def generate = Stream(i) }
case class GeneratorRandomInt(i: Int) extends Generator[Int] { def generate = ... }

ConstantInt(1) + ConstantInt(2) // (3) ok
ConstantBool(true) && ConstantBool(false) // (4) ok

ConstantInt(1) + ConstantBool(false) // (5)
ConstantBool(true) + ConstantInt(1) // (6) compiles but it's bad

ConstantBool(true) && ConstantInt(1) // (7)
ConstantInt(1) && ConstantBool(true) // (8) compiles but it's bad

I would like (1) and (2) to raise a compiler exception if they are not applied in a right scheme. For example, although (6) and (8) currently compiles, they should not. (5) and (7) already do not compile. How to specify this type condition to which to apply those methods?

Upvotes: 4

Views: 247

Answers (1)

Malte Schwerhoff
Malte Schwerhoff

Reputation: 12852

You can use generalised type constraints (see this answer) to achieve what you want:

trait Generator[A] {
  def generate: Stream[A]

  def +(other: Generator[A])(implicit evidence: A =:= Int): Generator[Int] = ???
  def &&(other: Generator[A])(implicit evidence: A =:= Boolean): Generator[Boolean] = ???
}

case class GeneratorInt(i: Int) extends Generator[Int] { def generate = Stream(i) }
case class GeneratorBool(i: Boolean) extends Generator[Boolean] { def generate = Stream(i) }

GeneratorInt(1) + GeneratorInt(2) // (3) ok
GeneratorBool(true) && GeneratorBool(false) // (4) ok

GeneratorInt(1) + GeneratorBool(false) // (5) type mismatch
GeneratorBool(true) + GeneratorInt(1) // (6) type mismatch

GeneratorBool(true) && GeneratorInt(1) // (7) type mismatch
GeneratorInt(1) && GeneratorBool(true) // (8) type mismatch

Upvotes: 11

Related Questions