Как избавиться от ударов с помощью синхронной отправки с Scala Actors? - PullRequest
3 голосов
/ 19 июня 2011

Предположим, я отправляю запрос актеру и получаю его ответ синхронно:

case class MyRequest()
case class MyResponse(data:Any)

val resp = myActor !? MyRequest()

Теперь я должен опустить resp до MyResponse, чтобы получить доступ data

val data = (resp.asInstanceOf[MyResponse]).data

Как я могу избавиться от приведения и написать код безопасным способом? (Я думаю, что вместо этого я могу использовать сопоставление с образцом, но я бы предпочел другое решение).

Ответы [ 4 ]

4 голосов
/ 20 июня 2011

Насколько я знаю, актеры Scala не набираются. Так что используйте актеров Akka, которые поддерживают типизированных актеров . Я также сталкивался с вашей проблемой раньше и обрабатывал ее с неявным манифестом, чтобы получить некоторые типизированные актеры в scala.

3 голосов
/ 20 июня 2011

Актеры не набираются, поэтому нет возможности сделать это с базовой библиотекой. Однако есть несколько способов создания служебных методов для него.

Вы можете закодировать тип результата в сообщении и затем использовать служебный метод, который использует это:

trait Result[T]

class RichActor(self: Actor) {
    def !?![T](msg: Result[T]): T = (self !? msg).asInstanceOf[T]

    def !?![T](timeout: Long, msg: Result[T]) = (self.!?(timeout, msg)).asInstanceOf[Option[T]]
}

implicit def enrichActor(a: Actor) = new RichActor(a)

использование:

case class Message() extends Result[Int]

val i = actor !?! Message()

Тип i является Int

Примечание: ранее размещено здесь: http://www.tikalk.com/java/blog/type-safe-actor-messages

3 голосов
/ 20 июня 2011

Сопоставление с шаблоном - это обычный способ извлечения данных из сообщений и определения типа сообщений, которые вы получили.

Я бы просто использовал здесь сопоставление с образцом, и мне любопытно, почему вы бы предпочли другое решение.

1 голос
/ 15 сентября 2012

На этот комментарий к моему предыдущему ответу Я хочу поделиться своим решением с манифестами, используя актеров akka:

import akka.actor._

trait Message
case class Foo(i: Int) extends Message
case class Bar(b: Boolean) extends Message
case class Baz(s: String) extends Message

case class MSG[C](content: C)(implicit m: Manifest[C]) {
  def manifest = m
  @inline def asMsg[C] = this.asInstanceOf[MSG[C]]
}

class MyActor[C](implicit manifest: Manifest[C]) extends Actor {
  private val behaviour: C =*> Unit = {
    case foo @ Foo(i) => println(foo) // i: Int
    case bar @ Bar(b) => println(bar) // b: Boolean
  }
  def receive = {
    case m: MSG[_] if m.manifest <:< implicitly[Manifest[C]] && behaviour.isDefinedAt(m.asMsg[C].content) => behaviour(m.asMsg[C].content)
    case m => println("wrong message : " + m)
  }
}

val system = ActorSystem("MySystem")
val myActor = system.actorOf(Props(new MyActor[Message]()), name = "myactor")
myActor ! MSG(Foo(1)) //  Foo(1)
myActor ! MSG(Bar(true)) // Bar(true)
myActor ! MSG(Baz("!")) // wrong message : MSG(Baz(!))
myActor ! Foo(2) // wrong message : Foo(2)
myActor ! MSG("?") // wrong message : MSG(?)
system.shutdown

Кроме того, я попытался использовать это с параметризованными типами:

sealed trait Parameterized1[X] // only marker
case class Foo1[X](x: X) extends Parameterized1[X]

case class MSG1[X, Z[X]](content: Z[X])(implicit mX: Manifest[X]) {
  def manifestX = mX
  @inline def asMsg[A, C[A]] = this.asInstanceOf[MSG1[A, C]]
}

class MyActor1[X, Z[X]](implicit mX: Manifest[X]) extends Actor {
  private val behaviour: Z[X] =*> Unit = {
    case foo @ Foo1(x: X) => println(foo)
  }
  def receive = {
    case m: MSG1[_, _] if m.manifestX <:< implicitly[Manifest[X]] && behaviour.isDefinedAt(m.asMsg[X, Z].content) => behaviour(m.asMsg[X, Z].content)
    case m => println("wrong message : " + m)
  }
}

val system1 = ActorSystem("MySystem1")
val myActor1 = system1.actorOf(Props(new MyActor1[Int, Parameterized1]()), name = "myactor1")
myActor1 ! MSG1(Foo1(1)) // Foo1(1)
myActor1 ! MSG1(Foo1("A")) // wrong message : MSG1(Foo1(A))
myActor1 ! Foo1(1) // wrong message : Foo1(1)
system1.shutdown

Или даже с двумя параметрами типа:

sealed trait Parameterized2[X, Y] // only marker
case class Foo2[X, Y](x: X, y: Y) extends Parameterized2[X, Y]
case class Bar2[X, Y](x: X, y: Y) extends Parameterized2[X, Y]

case class MSG2[X, Y, Z[X, Y]](content: Z[X, Y])(implicit mX: Manifest[X], mY: Manifest[Y]) {
  def manifestX = mX
  def manifestY = mY
  @inline def asMsg[A, B, C[A, B]] = this.asInstanceOf[MSG2[A, B, C]]
}

class MyActor2[X, Y, Z[X, Y]](implicit mX: Manifest[X], mY: Manifest[Y]) extends Actor {
  private val behaviour: Z[X, Y] =*> Unit = {
    case foo @ Foo2(x: X, y: Y) => println(foo)
    case bar @ Bar2(x: X, y: Y) => println(bar)
  }
  def receive = {
    case m: MSG2[_, _, _] if m.manifestX <:< implicitly[Manifest[X]] && m.manifestY <:< implicitly[Manifest[Y]] && behaviour.isDefinedAt(m.asMsg[X, Y, Z].content) => behaviour(m.asMsg[X, Y, Z].content)
    case m => println("wrong message : " + m)
  }
}

val system2 = ActorSystem("MySystem2")
val myActor2 = system2.actorOf(Props(new MyActor2[Int, String, Parameterized2]()), name = "myactor2")
myActor2 ! MSG2(Foo2(1, "A")) // Foo2(1,A)
myActor2 ! MSG2(Foo2(1, true)) // wrong message : MSG2(Foo2(1,true))
myActor2 ! Foo2(1, "A") // wrong message : Foo2(1,A)
myActor2 ! MSG2((1, "A")) // wrong message : MSG2((1,A))
myActor2 ! MSG2(Bar2(2, "B")) // Bar2(2,B)
system2.shutdown
...