How do I get around type erasure on Akka receive m

2019-07-18 03:27发布

I'm using cake pattern in Scala 2.10 to inject required trait to my actor acording to some bussiness logic:

I have several types f Events:

 sealed abstract class Event(val timeStamp:Long)
  case class StudentEvent(override val timeStamp:Long, studentId:Long)  extends Event(timeStamp:Long)
  case class TeacherEvent(override val timeStamp:Long, teacherIdId:Long)  extends Event(timeStamp:Long)

Now I have traits which implement action for each type of event:

Abstract:

trait Action[T <: Event] {   
  def act[T](event:T):Unit
}

And two impls:

trait StudentAction extends Action[StudentEvent]{
  override def act[StudentEvent](event:StudentEvent):Unit = println(event)
}

and

trait TeacherAction extends Action[TeacherEvent]{
  override def act[TeacherEvent](event:TeacherEvent):Unit = println(event)
}

Now my Actor:

class ActionActor[T <: Event] extends Actor{
  self:Action[T]=>

  override def receive= {
    case msg: T => act(msg)
    case _ => println("Unknown Type")
  }
}

And I inject required trait this way:

 val actionActor =  system.actorOf(Props(new ActionActor[StudentEvent] with StudentAction))
 actionActor ! StudentEvent(1111L,222L)

On compile I'm getting error:

Warning:(14, 14) abstract type pattern T is unchecked since it is eliminated by erasure
    case msg:T => act(msg)
         ^

I know that somehow I need to use TypeTag, but I failed to understand how can I do it.

Please help.

Update:

In reality, I have 10 type of events which extends from Event that I need to handle.

I want to implement business logic for each event in separate trait, because because mixing all 10 event handler functions will give me several hundreds(if not thousands) lines of code.

I don't want to create different Actor types for each event. For example:

class Event1Actor extend Actor{
  def receive ={
     case Event1(e) => //event1 Business Logic
   }
}

class Event2Actor extend Actor{
  def receive ={
     case Event2(e) => //event2 Business Logic
   }
}  

and the same Event3Actor, Event4Actor,etc....

Such code seems ugly to me, because I need to implement business Logic inside each Actor.

I'm seeking for some kind generic solution based on design pattern, for example strategy pattern.

1条回答
forever°为你锁心
2楼-- · 2019-07-18 04:21

First, I would suggest defining your events as:

sealed trait Event { def timeStamp: Long }
case class StudentEvent(timeStamp: Long, studentId: Long)
  extends Event
case class TeacherEvent(timeStamp: Long, teacherId: Long)
  extends Event

This is the standard encoding for algebraic data types.

Second, this business where you're using a self-type seems really confusing to me. Surely the same object shouldn't be both an "actor" and an "action"? Why not use composition here instead of inheritance? (It kinda feels like you're just throwing Scala features together. So a general piece of advice here would be: slow down.)

But to address your main question, I think you're fundamentally on the wrong path. Almost any time you end up butting heads with type erasure and/or TypeTag, it's a sign that your design is flawed and you should back up and rethink — unless you realllllllly know what you're doing.

In any case, you're not going to be able to get Akka to attach a TypeTag to your messages. Akka just doesn't work that way. (There have been a series of efforts to add "typed actors" or "typed channels" to Akka, and you might look those up, but it's probably overkill in your use case unless you're realllllly sure it isn't.)

What is the underlying architectural problem that you're trying to solve here? What motivated this design?

Check out Composing trait behavior in Scala in an Akka receive method, it seems really similar, and after digesting it, I think you'll be in a better position to decide what to do next.

查看更多
登录 后发表回答