Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Created October 29, 2014 12:25
Show Gist options
  • Save thomasdarimont/7cd7cb07613d732b57f9 to your computer and use it in GitHub Desktop.
Save thomasdarimont/7cd7cb07613d732b57f9 to your computer and use it in GitHub Desktop.
akka Actor API and Actor DSL example for ping pong
package example
import akka.actor._
case object PingMessage
case object PongMessage
case object StartMessage
case object StopMessage
class Ping(pong: ActorRef) extends Actor {
var count = 0
def incrementAndPrint {
count += 1; println("ping")
}
def receive = {
case StartMessage =>
incrementAndPrint
pong ! PingMessage
case PongMessage =>
incrementAndPrint
if (count > 99) {
sender ! StopMessage
println("ping stopped")
context.stop(self)
} else {
sender ! PingMessage
}
}
}
class Pong extends Actor {
def receive = {
case PingMessage =>
println(" pong")
sender ! PongMessage
case StopMessage =>
println("pong stopped")
context.stop(self)
}
}
/**
* @author Thomas Darimont
*/
object AkkaActorApiExample extends App{
println("Akka scala example")
val system = ActorSystem("PingPongSystem")
val pong = system.actorOf(Props[Pong], name = "pong")
val ping = system.actorOf(Props(new Ping(pong)), name = "ping")
// start them going
ping ! StartMessage
}
/*
As you can see from the import statement, I'm using Akka actors,
not the traditional/older Scala actors.
When you implement an Akka actor, you define the receive method,
and implement your desired behavior in that method.
Messages between actors should be immutable, and
case classes are great for this purpose.
The code in my PingPongTest class shows the two ways to create Akka actors
(see the 'val pong' and 'val ping' lines.) I've written more
about this in my Simple Scala Akka actors examples tutorial.
I get everything started by sending a StartMessage to my "ping" actor.
After that, the two actors bounce messages back and forth as fast as they can until they stop.
*/
package example
import akka.actor.ActorDSL._
import akka.actor.{Props, ActorSystem}
case object PingMessage
case object PongMessage
case object StartMessage
case object StopMessage
/**
* @author Thomas Darimont
*/
object AkkaActorDslExample extends App{
println("Akka scala example")
implicit val system = ActorSystem("PingPongSystem")
var pong = actor(new Act{
become {
case PingMessage =>
println(" pong")
sender ! PongMessage
case StopMessage =>
println("pong stopped")
context.stop(self)
}
})
var ping = actor(new Act{
var count = 0
def incrementAndPrint {
count += 1; println("ping")
}
become {
case StartMessage =>
incrementAndPrint
pong ! PingMessage
case PongMessage =>
incrementAndPrint
if (count > 99) {
sender ! StopMessage
println("ping stopped")
context.stop(self)
} else {
sender ! PingMessage
}
}
})
// start them going
ping ! StartMessage
}
@ignasi35
Copy link

Everything looks great in both code samples.

If I were to review something I'd get rid of Start and StopMessage. Actors should not have that knowledge.
Then, from the 'main' code you could already send a ping to pingActor and have the actors start their thing.
that still leaves the ending open... well, let them crash! If Ping has a ref to Pong, then Ping could context.watch(pong) and implement 'case Terminated(pong)'. Then, I'd move the logic if(x>99) to pong and have pong suicide when 100 balls exchanged (like you already do with context.stop(self)). Upon pong's suicide, ping would run the case Terminate and should suicide too.
In that case you should call them Romeo and Juliet.

Also, I'd get rid of the XxxxMessage notation. You can create an object and put the case objects inside. For example:

object PingPongDialect { // or PingPong 
   case object Ping
   case object Pong
}

In occasions, and actually here it may be a good option, message case object/class are placed in a companion object of the actor so if I know I'm sending s/thin to Pong, I can do Pong.Reverse or Pong.Smash, etc...

There's a thing I hate about the ping pong example is that it's misleading to me. I don't have any friend who are named Ping nor Pong. Also, in this example Ping and Pong (created as different Actors) should be the same class definition and main should instantiate two. Then, messages would not be Ping and Pong, they would be something more interesting: Reverse, Trick, Smash, etc... ;-). But that's an opinion that has nothing to do with scala idiomacy.

About the second example, it looks great to me. I was not aware of the ActorDSL and it looks quite good actually. I went and read the docs and I think I'll eventually use it.
It all looks pretty good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment