1

I have an actor that creates children actors of type Child1 in this example. Child1 constructor takes two String's which are extracted from variables that reside in SomeTrait that is mixed into SomeActor isntance.

trait SuperTrait {
  lazy val str1: Option[String] = None
  lazy val str2: Option[String] = None
} 
trait SomeTrait extends SuperTrait {
  override lazy val str1: Option[String] = Some("Str1")
  override lazy val str2: Option[String] = Some("Str2")
}
class SomeActor extends Actor {
  this: SuperTrait => 
  var child: Option[ActorRef] = None
  override def preStart(): Unit = {
    child = for {
      st1 <- str1 
      st2 <- str2
    } yield context.actorOf(Child1.props(st1, st2)))
  }
}

Creation on SomeActor instance:

context.actorOf(Props[SomeActor with SomeTrait])

With this I am getting strange error:

SomeActor cannot be cast to SomeTrait.

It seems that extracting variables from Option container of SomeTrait throws that exception.

What am I missing here?

And it doesn't only happen with for comprehensions. Also when I try to do str1.getOrElse("") or add a getter to SomeTrait : def getStr1 = str1.getOrElse("")

goral
  • 1,275
  • 11
  • 17
  • 1
    How are you creating instances of `SomeActor`? `new SomeActor with SomeTrait`? Why not just `class SomeActor extends Actor with SomeTrait` and leave off the self type? – ggovan Jun 30 '14 at 15:46
  • Ok, it works when I extend the trait and not mix it in. Do you know why? I chose to mix it in for usefulness reasons that are not important now. – goral Jun 30 '14 at 15:49
  • I don't know much about Akka otherwise I would post an answer, but I would assume that Akka is creating instances of _SomeActor_ through reflection or some other means that doesn't allow `scalac` to enforce that instances conform to self types. You could make _SomeActor_ abstract to see if this is the case. – ggovan Jun 30 '14 at 16:09
  • I've found another issue. Let's say `SomeTrait` is a subtrait of `SupSomeTrait` then during runtime values of `str1` and `str2` are taken from `SupSomeTrait`. That's very inconvenient. – goral Jun 30 '14 at 16:15
  • @goral: this has to do with the order traits are initialized. If you want a sane initialization order, always use `lazy val` or `def` in traits, never `val`. – Mario Camou Jun 30 '14 at 17:49
  • @Mario I changed it to def and it is still not working. – goral Jun 30 '14 at 19:33
  • The initialization of `str1` and `str2` is not working correctly? That's weird... Or is it the original question that isn't working? In that case check my answer below. – Mario Camou Jun 30 '14 at 19:39
  • yep, I tried with `def` and `lazy val` and it's still trying to get them from parent trait. I will give a try to your answer below. – goral Jul 01 '14 at 08:45

1 Answers1

3

As @ggovan said above, when using Akka you can't control the construction of an Actor. The Akka library takes care of that (and that's why you have Props to encapsulate the parameters passed to the constructor). The reason for this is, if your actor crashes its supervisor needs to be able to create a new instance of it.

When you use Props[X], Scala is using a a ClassTag to find the runtime class of the Actor. However, the ClassTag doesn't seem to pick up the anonymous class created when you call Props[X with Y], it picks up only the base class (in your case, SomeActor). A way around that is to use the by-name alternative of Props, so you can do this when creating the SomeActor:

val actor = sys.actorOf(Props(new SomeActor with SomeTrait))

That works and it picks up the overridden lazy vals too. Be sure, however, to read the downsides to that at http://doc.akka.io/docs/akka/snapshot/scala/actors.html (section "Dangerous variants").

Another option would be to use the Cake Pattern:

trait SomeTrait {
  lazy val str1: Option[String] = None
  lazy val str2: Option[String] = None
}

trait ActorTrait extends Actor {
  this: SomeTrait => 
  override lazy val str1 = Some("Str1")
  override lazy val str2 = Some("Str2")
  var child: Option[ActorRef] = None
  override def preStart(): Unit = {
    child = for {
      st1 <- str1 
      st2 <- str2
    } yield context.actorOf(Child1.props(st1, st2)))
  }
}

class SomeActor extends ActorTrait with SomeTrait

In that case you can use the recommended Props[SomeActor] method to create the Props object.

Mario Camou
  • 2,303
  • 16
  • 28
  • Supervisor of the actor creates it extending proper traits. Like this `context.actorOf(Props[SomeActor with SomeTrait])`. It still takes values from super trait. I've edited question to add that. – goral Jul 01 '14 at 10:51
  • I have been using scenario for creating an actor shown in akka documentation but it still doesn't help. I somehow made a workaround combining this and Cake Pattern you proposed but I lost modularity at the same time. For the time being it's working but is not a solution I am proud of. – goral Jul 01 '14 at 13:25