3
votes

I am new to Scala programming and having some trouble understanding how actors works and how to use them correctly.

Looking at the source code of an Akka actor, the following is exposed:

trait Actor {
  def receive: Actor.Receive // Actor.Receive = PartialFunction[Any, Unit]
}

My first impression of this is that Actor is a trait that exposes one abstract method, receive which takes no arguments and then returns a partial function. First question, is this the correct interpretation of the API?

Next, I looked at the documentation for how to implement actors. The examples look something like this:

class HelloActor extends Actor {
  def receive = {
    case "hello" => println("hello back at you")
    case _       => println("huh?")
  }
}

Clearly there is some pattern matching going on here, but I'm having trouble understanding how this works. For instance, let's say I wanted to invoke the receive method directly without using something like send, how would I do it?

2
The body of receive is a literal declaration of a PartialFunction. Why would you want to invoke this directly? - Michael Zajac
Because I'm curious about how it works. - Max

2 Answers

3
votes

as others already answered, you never should directly call methods on Actors. But your question seems to be more about "how does PartialFunction work in Scala?", right?

In Scala the PartialFunction[In, Out] has a bit of compiler generated code for methods like isDefinedAt, so you can ask a partial function if it's defined for a certain argument:

scala> val fun: PartialFunction[Any, String] = {
     | case 42 => "yes"
     | }
fun: PartialFunction[Any,String] = <function1> 

scala> fun.isDefinedAt(12)
res0: Boolean = false

scala> fun.isDefinedAt(42)
res1: Boolean = true

scala> fun(42)
res2: String = yes

scala> fun(12)
scala.MatchError: 12 (of class java.lang.Integer)

```

We use the isDefinedAt method before we apply a message to a receive function, and if it's not able to handle the message we pass it into unhandled(msg), which usually just logs the message to dead letters so you can figure out that your Actor didn't handle a message.

2
votes

For instance, let's say I wanted to invoke the receive method directly without using something like send, how would I do it?

You would not.

Directly invoking the receive function of an actor without going through its ActorRef breaks the guarantees of the actor model:

  1. Actors process exactly one message at a time.
  2. An actor's internal state can only be mutated from the receive function.

(among others, see What is an actor?)

When you send an ActorRef a message, you aren't directly talking to the actor. Hidden behind the ActorRef are any number of implementation details—you could be sending a message to a router or a remote actor or a dead actor (or all three!). Isolating an actor's details behind this reference allows the guarantees that are the entire point of the actor model.