Scala Newb Scala Newb - 1 month ago 7
Scala Question

"Sharing" parameterized types

I know there's a simple solution to this. But I can't think of it.

I've got a general Queue that happens to be an Actor, type-parameterized on what it contains. I need a case class Add message to send data to the queue, which should enforce the same type.

We can do this:

class Queue[A] extends Actor {
case class Add[A](items: Seq[A])
...

The problem with this is that we can only create an instance of Add from an instance of Queue, and clients, who know only of the ActorRef, will not have such an instance.

If we move Add outside of Queue, that problem disappears, but then of course the compiler has no idea that Add's type parameter and Queue's type parameter are related in any way. Type-casting will be necessary to combine collections of the two.

Is there a simple way to tell the compiler that type parameters in two unrelated classes are related?

Answer

You'll need to do a cast / type check when you receive the Add[_] message in a Queue[A] actor, as the sender doesn't know the type of the actor, so it cannot be enforced that they sent an Add[A] and not an Add[B]

This is going to be awkward because of type erasure

It's also rather awkward to put the message as a type-dependent class inside the actor, as you'll find it difficult to construct.

It can be done, but I think you'd be better of simplifying the design. Here you go:

import Queue.Add
import akka.actor.{Actor, ActorSystem, Props}
import scala.reflect.runtime.universe._

class Queue[A]()(implicit tt: TypeTag[A]) extends Actor {

  override def receive = {
    case a@ Add(items) if a.tpe.tpe <:< typeOf[A] =>
      println("added: " + items)

    case other =>
      println("bad message: " + other)
  }
}

object Queue {
  case class Add[A](
    items: List[A])(
    implicit val tpe: TypeTag[A])
}

object Main {
  def main(args: Array[String]): Unit = {

    val system = ActorSystem()

    val stringQueue = system.actorOf(Props(new Queue[String]()))

    stringQueue ! Add(List("a","b"))
    stringQueue ! Add(List(1,2,3))
  }
}
Comments