4

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?

Mikaël Mayer
  • 10,425
  • 6
  • 64
  • 101
  • 2
    I'd put the `+` and `&&` methods in their respective subtypes, and also set `A` as covariant so that `Generator[A] <: Generator[B]` iif `A <: B`. How does it sound? – F.X. May 27 '13 at 08:31
  • 1
    I reformulated the problem so that this trivial solution does not work. – Mikaël Mayer May 27 '13 at 10:13

1 Answers1

11

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
Community
  • 1
  • 1
Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71
  • I editted my problem. Is it now possible to get rid of the cast to Generator[Int] in the + method? – Mikaël Mayer May 27 '13 at 10:13
  • 1
    @MikaëlMayer Please try it yourself before asking follow-up questions. Had you done it, you'd seen that a) your code contains errors, for example, `CustomGenerator` should probably be `CustomGeneratorInt` and `zipWith` should probably be `zip`, and that b) the cast is redundant. – Malte Schwerhoff May 27 '13 at 13:11
  • Sorry for that. zipWith already contains a map, I corrected my question (which is some piece of code I simplified to write not too much). Why do you say that the case is redundant? – Mikaël Mayer May 27 '13 at 14:06
  • 1
    @MikaëlMayer Because you can remove the cast if you "guard" `+` and `&&` with generalised type constraints. – Malte Schwerhoff May 27 '13 at 14:15