Created
November 22, 2013 10:36
-
-
Save ktonga/7597912 to your computer and use it in GitHub Desktop.
Monad aproach for receive pipelining.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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