scala actor message definition - scala

Do i need to define class for message i want to retrieve on a scala actor?
i trying to get this up
where am i wrong
def act() {
loop {
react {
case Meet => foundMeet = true ; goHome
case Feromone(qty) if (foundMeet == true) => sender ! Feromone(qty+1); goHome
}}}

You can think it as a normal pattern matching just like the following.
match (expr)
{
case a =>
case b =>
}
So, yes, you should define it first, use object for Message without parameters and case class for those has parameters. (As Silvio Bierman pointed out, in fact, you could use anything that could be pattern-matched, so I modified this example a little)
The following is the sample code.
import scala.actors.Actor._
import scala.actors.Actor
object Meet
case class Feromone (qty: Int)
class Test extends Actor
{
def act ()
{
loop {
react {
case Meet => println ("I got message Meet....")
case Feromone (qty) => println ("I got message Feromone, qty is " + qty)
case s: String => println ("I got a string..." + s)
case i: Int => println ("I got an Int..." + i)
}
}
}
}
val actor = new Test
actor.start
actor ! Meet
actor ! Feromone (10)
actor ! Feromone (20)
actor ! Meet
actor ! 123
actor ! "I'm a string"

Strictly no, you can use any object as the message value. A message could be an Int, String or a Seq[Option[Double]] if you like.
For anything but play-around code I use custom immutable message classes (case-classes).

Related

What's the Akka-typed equivalent to pipeTo?

I'm currently trying to rewrite an existing untyped actor into a typed one. Since the actor is talking to a MySQL database using ScalikeJDBC, and since I'd like to have that done asynchronously, I'm dealing with Futures coming out of a separate (non-actor) repository class.
With untyped Akka, in an actor's receive method, I could do this:
import akka.pattern.pipe
val horseList : Future[Seq[Horse]] = horseRepository.listHorses(...)
horseList pipeTo sender()
And the sender actor would eventually receive a list of horses. I can't figure out how to do this inside a Behaviour, like:
val behaviour : Behavior[ListHorses] = Behaviors.receive {
(ctx,msg) => msg match {
case ListHorses(replyTo) =>
val horseListF : Future[Seq[Horse]] = horseRepository.listHorses(...)
// -> how do I make horseListF's content end up at replyTo? <-
Behaviors.same
}
}
The pipe pattern doesn't work (as it expects an untyped ActorRef), and so far I haven't found anything else in the akka-actor-typed (2.5.12) dependency I'm using to make this work.
How do I do this?
In Akka 2.5.22 (maybe earlier) there is context.pipeToSelf:
def pipeToSelf[Value](future: Future[Value])(mapResult: Try[Value] => T): Unit
You still have to provide a pattern match for Success and Failure, which in my code I've reduced with this sugar:
def mapPipe[A, T](success: A => T, failure: Throwable => T): Try[A] => T = {
case Success(value) => success(value)
case Failure(e) => failure(e)
}
Resulting in a call like this:
case class Horses(horses: Seq[Horse]) extends Command
case class HorseFailure(e: Throwable) extends Command
...
context.pipeToSelf(horseList) {
mapPipe(Horses,HorseFailure)
}
You can simply send a message to replyTo when the future completes successfully:
case ListHorses(replyTo) =>
horseRepository.listHorses(...) foreach { horses => replyTo ! horses }
Behaviors.same
Or if you want to handle errors as well:
case ListHorses(replyTo) =>
horseRepository.listHorses(...) onComplete {
case Success(horses) => replyTo ! horses
case Failure(e) => // error handling
}
Behaviors.same
In order for this to work, you need an ExecutionContext. It usually makes sense to use the same one as the actor, so you will have to make it available to onComplete or foreach first:
implicit val ec = ctx.executionContext

Akka: Future to Actor communication?

I have a system that spawns a single actor who will spawn many futures. Some of these futures will run into scenarios that need to spawn more futures (but tell the actor about it). How do I send a message from a future to an actor on the completion of the future's operations?
I've looked at the pipeTo documentation but I am having trouble referencing the actors in my system in my future class.
Here is what my Future class looks like:
class crawler(string: String) {
val status: Future[Boolean] = Future[Boolean] {
//Do something with content
println("I am a future working on cert crawling. My cert contents are: " + cert.content)
true
}
status onComplete {
case Success(true) =>
for(chars <- string.toCharArray) {
//send actor a message for each character of the string.
}
case Failure(t) => println("An error has occured: " + t.getMessage)
}
}
Where the actor's receive method does the following:
def receive = {
case c:Char => if(!certCache.containsKey(c)){
println("actor >>>> Need to begin crawl on " + c + ".")
sender() ! new crawler("give sender the future")
case _ => println("That's not the right input!")
}
And, my Actor is spawned like:
object Main extends App {
val system = ActorSystem("MySystem")
val actor = system.actorOf(Props[actorClass], name = "actor")
actor ! 'a'
}
Directly
You could dependency inject the ActorRef into your Future (not recommended, see Abstracted) :
import akka.actor.ActorRef
//dependency injection of the ActorRef with a default value of noSender
class crawler(string : String, actorRef : ActorRef = ActorRef.noSender) {
...
status OnComplete {
//send each Char in string to the actorRef
case Success(true) => string.foreach(actorRef ! _)
...
}
Then in your Actor you can use self to pass the ActorRef into the crawler:
def receive = {
case c : Char => if(!certCache.containsKey(c)) {
sender() ! new crawler("give sender the future", self)
}
}
Abstracted
Further, you could abstract away the use of ActorRef entirely so that crawler doesn't need to know the details of messaging passing. This is the more "functional" approach which has the benefit of being extendable if you ever switch to Futures or even akka.stream.scaladsl.Source for reactive streams (see example):
//no akka imports or dependencies
class crawler(string : String, sendChar : (Char) => Unit) {
...
case Success(true) => string foreach sendChar
}
And in your Actor you can pass an anonymous function to crawler which sends a Char to the Actor via self:
def receive = {
case c : Char => if(!certCache.containsKey(c)) {
sender ! new crawler("give sender the future", self ! _)
}
}
You can even get robust and provide default "do nothing" behavior for your sendChar function:
class crawler(string : String, sendChar : (Char) => Unit = {_=>}) {
...
}
val crawler = crawler("foo") //still get regular Future behavior for status

Ask an actor and let him respond when he reaches a particular state in Akka 2

I'm quite new to Akka so my question may seem simple:
I have an actor called workerA that uses FSM and can thus be either in those two states Finishedand Computing:
sealed trait State
case object Finished extends State
case object Computing extends State
sealed trait Data
case object Uninitialized extends Data
case class Todo(target: ActorRef, queue: immutable.Seq[Any]) extends Data
When workerA receives GetResponse it should answer if and if only it is in state Finished.
What is the proper way of doing this? I know we should avoid to be blocking in this paradigm but here it is only the top actor which is concerned.
Thanks
I'm not necessarily sure you even need FSM here. FSM is a really good tool for when you have many states and many possible (and possibly complicated) state transitions between those states. In your case, if I understand correctly, you basically have two states; gathering data and finished. It also seems that there is only a single state transition, going from gathering -> finished. If I have this all correct, then I'm going to suggest that you simply use become to solve your problem.
I have some code below to show a trivial example of what I'm describing. The basic idea is that the main actor farms some work off to some workers and then waits for the results. If anyone asks for the results while the work is being done, the actor stashes that request until the work is done. When done, the actor will reply back to anyone that has asked for the results. The code is as follows:
case object GetResults
case class Results(ints:List[Int])
case object DoWork
class MainActor extends Actor with Stash{
import context._
override def preStart = {
val a = actorOf(Props[WorkerA], "worker-a")
val b = actorOf(Props[WorkerB], "worker-b")
a ! DoWork
b ! DoWork
}
def receive = gathering(Nil, 2)
def gathering(ints:List[Int], count:Int):Receive = {
case GetResults => stash()
case Results(i) =>
val results = i ::: ints
val newCount = count - 1
if (newCount == 0){
unstashAll()
become(finished(results))
child("worker-a") foreach (stop(_))
child("worker-b") foreach (stop(_))
}
else
become(gathering(results, newCount))
}
def finished(results:List[Int]):Receive = {
case GetResults => sender ! results
}
}
class WorkerA extends Actor{
def receive = {
case DoWork =>
//Only sleeping to simulate work. Not a good idea in real code
Thread sleep 3000
val ints = for(i <- 2 until 100 by 2) yield i
sender ! Results(ints.toList)
}
}
class WorkerB extends Actor{
def receive = {
case DoWork =>
//Only sleeping to simulate work. Not a good idea in real code
Thread sleep 2000
val ints = for(i <- 1 until 100 by 2) yield i
sender ! Results(ints.toList)
}
}
Then you could test it as follows:
val mainActor = system.actorOf(Props[MainActor])
val fut = mainActor ? GetResults
fut onComplete (println(_))
You can pattern match on FSM states:
// insert pattern matching stuff instead of ...
class MyActor extends Actor with FSM[State, Message] {
startWith(Finished, WaitMessage(null))
when(Finished) {
case Event(Todo(... =>
// work
goto(Computing) using Todo(...)
case Event(GetResponse(... =>
// reply: sender ! msg // or similar
}
/* the rest is optional. You can use onTransition below to send yourself a message to report status of the job: */
when(Busy) {
case Event(Finished(... =>
// reply to someone: sender ! msg // or similar
goto(Finished)
}
onTransition {
case Finished -> Computing =>
// I prefer to run stuff here in a future, and then send a message to myself to signal the end of the job:
self ! Finished(data)
}
An Edit to more specifically address the question:
class MyActor extends Actor with FSM[State, Message] {
startWith(Finished, WaitMessage(null))
when(Finished) {
case Event(Todo(... =>
// work
goto(Computing) using Todo(...)
case Event(GetResponse(... =>
// reply: sender ! msg // or similar
stay
}
initialize()
}

Use case classes as messages in Scala

Am following sample code in the Horstman book (pages 291-292) to define and use case classes
as messages in a simple Scala actor system.
The problem is the case classes are not being recognized in the receive pattern matching and control is falling through to the case _ => statement.
The code is listed below. Everything works with non case class messages.
SENDER: In actor Rcoord the act method is:
def act() {
alive(9000)
register('rcoord, self)
proc_reg.start // start the process register actor
loop {
try {
receive {
case 'process =>
process_counter += 1
process_number = process_counter
spawn_process(process_number, sprocs)
case class CreateTS(xxx: Int)
proc_reg ! CreateTS(process_number)
case 'stats =>
Console.println("received msg from client to view statistics")
//sender ! 'ok
case 'stop =>
Console.println("received msg that client is terminating")
//sender ! 'bye
} // end receive
}
catch
{
RECEIVER: In actor proc_reg the act method is:
def act() {
Console.println("process register started")
case class CreateTS(process_number: Int)
case class UpdateTS(process_number: Int)
loop {
receive {
case CreateTS(process_number) =>
Console.println("create TS msg received from process "+process_number)
register_proc(process_number)
case UpdateTS(process_number) =>
Console.println("update TS msg received for process "+process_number)
update_proc(process_number)
case _ =>
Console.println("sender is: "+sender.toString)
//Console.println("full msg is: "+Msg.toString)
} // end receive
} // end loop
} // end act()
You have 2 different classes with the same name CreateTS.
You are sending CreateTS defined in sender and trying to receive message of other type with the same name in receiver.
You should move declarations of case classes from method body so it will be in scope of both methods.
Note that scala actors are deprecated, you should use akka actors.

How can Scala actors return a value in response to a message?

There are plenty of examples of actors replying with another message back to the sender, but whilst browsing the API docs I noticed the !! and !? operators which are part of the CanReply trait (which seems to be new to 2.8: http://www.scala-lang.org/archives/rc-api/scala/actors/CanReply.html). I was therefore wondering whether it was just a case of having the receive/react block return a value, i.e. make the PartialFunction return type something other than Unit?
I'll start digging through the source to try to work out how they're meant to be used, but if anyone has any insight or knows of any more in-depth documentation or examples then I'd be most grateful.
Cheers,
Paul.
Replies can be sent with the method reply, as shown here:
import scala.actors._
class Reverser extends Actor {
def act() { Actor.loop { react {
case s: String => Thread.sleep(1000); reply(s.reverse)
case _ => exit()
}}}
}
There are three ways to explicitly accept the reply.
Use !!, which returns a Future, which is a container class that promises to give you the contents when you need them. It returns immediately, but if you actually ask for the contents, you have to wait until the other thread is done and fills the request.
Use !? without a timeout. Your code will pause for as long as it takes for the other thread to reply.
Use !? with a timeout. Your code will pause until it gets a reply or until the timeout expires, whichever comes first.
Here's an example of all three:
val r = new Reverser
r.start
val a = (r !! "Hi")
a() match {
case s: String => println(s)
case _ => println("Error A")
}
val b = r !? "Hello"
b match {
case s: String => println(s)
case _ => println("Error B")
}
val c = (r !? (500,"Howdy"))
c match {
case Some(s: String) => println(s)
case Some(_) => println("Error C")
case None => println("Too slow!")
}
r ! None // None isn't a string, so r will stop running
And if you run this you get
iH
elloH
Too slow!