Skip to content

Instantly share code, notes, and snippets.

@ktonga
Created November 22, 2013 10:36
Show Gist options
  • Save ktonga/7597912 to your computer and use it in GitHub Desktop.
Save ktonga/7597912 to your computer and use it in GitHub Desktop.
Monad aproach for receive pipelining.
package akka
import akka.actor.Actor
import akka.actor.Actor.Receive
import AroundReceive.Around
object AroundReceive {
type Around = (Receive, Any) => Unit
def apply(around: Around, inner: AroundReceive, aroundFactory: Around => Around): AroundReceive =
new ChainableAroundReceive(around, inner, aroundFactory)
def apply(around: Around): AroundReceive = new FinalAroundReceive(around)
}
sealed abstract class AroundReceive(val around: Around) {
def apply(receive: Receive, msg: Any) = around(receive, msg)
def outerMap(f: Around => Around): AroundReceive = AroundReceive(f(around), this, f)
def innerMap(f: Around => Around): AroundReceive
}
class ChainableAroundReceive(_around: Around, val inner: AroundReceive, val aroundFactory: Around => Around)
extends AroundReceive(_around) {
private def withInner(newInner: AroundReceive) = AroundReceive(aroundFactory(newInner.around), newInner, aroundFactory)
def innerMap(f: (Around) => Around): AroundReceive = {
def rec(ar: AroundReceive): AroundReceive = ar match {
case chainable: ChainableAroundReceive => chainable.withInner(rec(chainable.inner))
case _final => _final.outerMap(f)
}
rec(this)
}
}
class FinalAroundReceive(around: Around) extends AroundReceive(around){
// client code just wants to map the closest to final around
def innerMap(f: (Around) => Around): AroundReceive = outerMap(f)
}
trait ReceivePipeline extends Actor {
private var pipeline: AroundReceive = AroundReceive(super.aroundReceive)
def pipeline(f: AroundReceive => AroundReceive): Unit = {
pipeline = f(pipeline)
}
override protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = {
pipeline(receive, msg)
}
}
trait OuterInterceptor {
this: ReceivePipeline =>
pipeline {
_.outerMap { around =>
{(receive, msg) =>
around(receive, " ** OUTER ** " + msg + " ** OUTER ** ")
}
}
}
}
trait InnerInterceptor {
this: ReceivePipeline =>
pipeline {
_.innerMap { around =>
{(receive, msg) =>
around(receive, " ++ INNER ++ " + msg + " ++ INNER ++ ")
}
}
}
}
class MyInterceptedActor extends Actor with ReceivePipeline {
pipeline {
_.outerMap { around =>
{(receive, msg) =>
around(receive, " ++ ACTOR ++ " + msg + " ++ ACTOR ++ ")
}
}
}
def receive: Actor.Receive = {
case m => println(m)
}
}
object PipelineSample extends App {
import akka.actor.{Props, ActorSystem}
val system = ActorSystem("monads-rock")
val oneWayRef = system.actorOf(Props(new MyInterceptedActor with InnerInterceptor with OuterInterceptor), "one-way")
val theWayAroundRef = system.actorOf(Props(new MyInterceptedActor with OuterInterceptor with InnerInterceptor), "the-way-around")
oneWayRef ! "Message Decorated One Way"
theWayAroundRef ! "Message Decorated The Way Around"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment