tutorial programacion modelo actores scala akka traits

scala - programacion - ¿Cómo usar un patrón de rasgos apilables con actores Akka?



akka tutorial (4)

Estoy tratando de implementar un rasgo de Pub / Sub para mezclarlo con otros actores akka usando un rasgo apilable.

Aquí está lo que se me ocurrió:

trait PubSubActor extends Actor { abstract override def receive = super.receive orElse { case Subscribe(topic) => /* ... */ case Publish(topic, msg) => /* ... */ } } class MyActor extends Actor with PubSubActor { override def receive = { case SomeMessage(a, b, c) => /* ... */ } }

En ese momento, el compilador devuelve un error: error: anulando el método de recepción en el rasgo MyActor ... el método de recepción necesita modificadores de "anulación abstracta".

¿Me puedes explicar por qué esto no funciona? ¿Cómo puedo arreglarlo para que funcione?

¡Gracias!

ACTUALIZAR

Los siguientes trabajos:

trait PubSubActor extends Actor { abstract override def receive = super.receive orElse { case Subscribe(topic) => /* ... */ case Publish(topic, msg) => /* ... */ } } class MyActor extends Actor { override def receive = { case SomeMessage(a, b, c) => /* ... */ } } class MyActorImpl extends MyActor with PubSubActor

¿Pero por qué? ¿Por qué puedo obtener el comportamiento que quiero de esta manera pero no el otro? ¿Alguna razón? Parece que no puedo entender la diferencia subyacente entre estas dos muestras que hace la diferencia.


Hay una solución simple y concisa:

Defina un rasgo de recepción que encadena múltiples funciones de recepción usando orElse :

trait Receiving { var receivers: Receive = Actor.emptyBehavior def receiver(next: Actor.Receive) { receivers = receivers orElse next } def receive = receivers // Actor.receive definition }

Usar esto en actores es fácil:

trait PubSubActor extends Receiving { receiver { case Publish => /* I''m the first to handle messages */ } } class MyActor extends PubSubActor with Receiving { receiver { case SomeMessage => /* PubSubActor didn''t handle, I receive the message */ } }

Se llamará primera recepción de PubSubActor. Si el mensaje no se manejó, se pasará a recibir de MyActor.


Inténtalo al revés:

object Subscription { case object Subscribe case object Unsubscribe } trait Subscription { this: Actor => import Subscription._ var subscribers = Set.empty[ActorRef] def receive: Receive = { case Subscribe => subscribers += sender case Unsubscribe => subscribers -= sender } } class MyActor extends Actor with Subscription { def receive = super.receive orElse { case msg => // handle msg } }

Tenga en cuenta que esto todavía hace uso del patrón de rasgos apilables, que está oculto por el hecho de que he omitido el núcleo. Así que algo como esto todavía funcionaría (al menos creo que sí, ATM, no tengo tiempo para verificar si se compila).

class Core extends Actor { def receive = Actor.emptyBehavior } class MyActor extends Core with Subscription

Por cierto, puedes leer más sobre el patrón (no relacionado con los actores) here .


Para el primero, disculpe por mi inglés, creo que el punto es que el modificador de anulación abstracto requiere la presencia de una implementación concreta del método de recepción, pero en la primera construcción

class MyActor extends Actor with PubSubActor { override def receive = { case SomeMessage(a, b, c) => /* ... */ }}

no se hace

La razón es que el compilador de scala realiza la linealización para la herencia, por lo que en la cadena de métodos de recepción tenemos la siguiente secuencia:

1) override def receive = { case SomeMessage(a, b, c) => /* ... */ } 2) abstract override def receive = super.receive orElse { case Subscribe(topic) => /* ... */ case Publish(topic, msg) => /* ... */ } 3) then Actor.receive - it hasn''t an implementation

Por lo tanto, no se puede llamar a PubSubActor.receive porque usa super.receive, a su vez, super.receive se basa en Actor.receive, pero Actor.receive no tiene una implementación.

En la segunda construcción

class MyActor extends Actor { override def receive = { case SomeMessage(a, b, c) => /* ... */ }} class MyActorImpl extends MyActor with PubSubActor

hemos recibido la cadena de métodos

1)

abstract override def receive = super.receive orElse { case Subscribe(topic) => /* ... */ case Publish(topic, msg) => /* ... */ }

2)

override def receive = { case SomeMessage(a, b, c) => /* ... */ }

3) luego Actor.receive - no tiene una implementación

Así que PubSubActor.receive puede llamar exitosamente a super.receive

Información adicional:

here

Especificación del lenguaje Scala, ver 5.1.2.


Sin duda, puedes lograr lo que estás buscando con la función de actor compostable de Akka. Esto se describe un poco en Extending Actors utilizando el encadenamiento PartialFunction .

Primero, el código de infraestructura (directamente de la documentación):

class PartialFunctionBuilder[A, B] { import scala.collection.immutable.Vector // Abbreviate to make code fit type PF = PartialFunction[A, B] private var pfsOption: Option[Vector[PF]] = Some(Vector.empty) private def mapPfs[C](f: Vector[PF] => (Option[Vector[PF]], C)): C = { pfsOption.fold(throw new IllegalStateException("Already built"))(f) match { case (newPfsOption, result) => { pfsOption = newPfsOption result } } } def +=(pf: PF): Unit = mapPfs { case pfs => (Some(pfs :+ pf), ()) } def result(): PF = mapPfs { case pfs => (None, pfs.foldLeft[PF](Map.empty) { _ orElse _ }) } } trait ComposableActor extends Actor { protected lazy val receiveBuilder = new PartialFunctionBuilder[Any, Unit] final def receive = receiveBuilder.result() }

A continuación, los comportamientos que desea poder componer en actores:

trait PubSubActor { self:ComposableActor => receiveBuilder += { case Subscribe(topic) => /* ... */ case Publish(topic, msg) => /* ... */ } } trait MyActor { self:ComposableActor => receiveBuilder += { case SomeMessage(a, b, c) => /* ... */ } }

Y por último, un actor real que utiliza estos comportamientos compositivos:

class MyActorImpl extends ComposableActor with PubSubActor with MyActor