Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save stanislav-chetvertkov/506c5196a4df56f88cf5c62b308df941 to your computer and use it in GitHub Desktop.
Save stanislav-chetvertkov/506c5196a4df56f88cf5c62b308df941 to your computer and use it in GitHub Desktop.
Actors with protocols
//Requires scalaz core and shapeless
import akka.actor.Actor.Receive
import akka.actor._
import shapeless.{Coproduct, :+:, CNil}
import scalaz.{Tag, @@}
object Test extends App {
import scala.reflect.ClassTag
trait ActorWithProtocol[T] extends Actor {
def process(input: T): Unit
override def receive: Receive = {
case x => process(x.asInstanceOf[T])
}
}
type TypedProps[Protocol] = Props @@ ActorProtocol[Protocol]
implicit class ProtocolOps[T](it: TypedProps[T]) {
def spawnRoot(implicit system: ActorSystem): ActorRef @@ ActorProtocol[T] = {
val props: Props = Tag.unwrap(it)
val ref: ActorRef = system.actorOf(props)
Tag[ActorRef, ActorProtocol[T]](ref)
}
def spawn(implicit context: ActorContext): ActorRef @@ ActorProtocol[T] = {
val props: Props = Tag.unwrap(it)
val ref: ActorRef = context.actorOf(props)
Tag[ActorRef, ActorProtocol[T]](ref)
}
}
trait ActorProtocol[T]
type ISB = Int :+: String :+: Boolean :+: CNil
implicit class ProtoOps[T](it: ActorRef @@ ActorProtocol[T]) {
def $(message: T)(implicit sender: ActorRef): Unit = {
val sendTo: ActorRef = Tag.unwrap(it)
sendTo.tell(message, sender)
}
}
//------------------------------//
class ActorB extends ActorWithProtocol[ISB] {
override def process(input: ISB): Unit = {
input.select[Int].foreach { i: Int =>
println(s"GOT int: $i")
}
input.select[String].foreach { i: String =>
println(s"GOT String: $i")
}
}
}
object ActorB {
def props:TypedProps[ISB] = {
Tag[Props, ActorProtocol[ISB]](Props(classOf[ActorB]))
}
}
//------------------------------//
class ActorC extends ActorWithProtocol[List[String]] {
override def process(input: List[String]): Unit = {
println(input)
}
}
object ActorC {
def props:TypedProps[List[String]] = {
Tag[Props, ActorProtocol[List[String]]](Props(classOf[ActorC]))
}
}
//------------------------------//
class MainActor(replyTo: ActorRef @@ ActorProtocol[ISB],
replyTo2: ActorRef @@ ActorProtocol[List[String]]) extends Actor {
override def preStart(): Unit = {
replyTo $ Coproduct[ISB]("123")
replyTo2 $ List("first", "second")
// will fail
// replyTo2 $ Some("123")
}
override def receive: Receive = {
case _ =>
}
}
object ActorA {
def props(replyTo: ActorRef @@ ActorProtocol[ISB],
replyTo2: ActorRef @@ ActorProtocol[List[String]]): Props = {
Props(classOf[MainActor], replyTo, replyTo2)
}
}
implicit val system: ActorSystem = ActorSystem()
val ref: ActorRef @@ ActorProtocol[ISB] = ActorB.props.spawnRoot
val ref2: ActorRef @@ ActorProtocol[List[String]] = ActorC.props.spawnRoot
system.actorOf(ActorA.props(ref,ref2)) //untyped
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment