Mehran
Mehran

Reputation: 16821

PartialFunction vs Function1 syntax-wise

As part of my journey learning Scala and then Akka, I'm trying to understand how one can spot a PartialFunction.

In the "Programming Reactive Systems" course, there is this example for an Actor:

class Toggle extends Actor {
  def happy: Receive = {
    case "How are you?" =>
      sender ! "happy"
      context become sad
  }
  def sad: Receive = {
    case "How are you?" =>
      sender ! "sad"
      context become happy
  }
  def receive = happy
}

It's a pretty simple one and I would say I understand what it is trying to do. The thing that I don't understand is that happy and sad are of type Function1 (or at least that's my belief) and yet they can play the role of a PartialFunction (receive needs a PartialFunction).

And even worse than that, based on the Akka documentation, receive is supposed to return a PartialFunction (not being one):

abstract def receive: Actor.Receive

Scala API: This defines the initial actor behavior, it must return a partial function with the actor logic.

But as far as I can tell, happy and sad are not returning a PartialFunction, they are one.

To wrap up my questions:

  1. Is there a way to spot a PartialFunction against Function1?
  2. Am I reading the example wrong? Isn't the receive a PartialFunction that returns Unit? If the answer is yes, then why the documentation says the receive should return a PartialFunction?

[UPDATE]

Based on the answer I've got from @Brian McCutchon, now I know that the receive method is supposed to return a PartialFunction. But that doesn't help with my confusion at all! Consider the following scenario:

def someFunction(): Receive = {
  //Instantiates and returns a Receive object
}

class Toggle {
  def happy: Receive = {
    case "How are you?" =>
      someFunction()
  }
}

My question is, how does the Scala compiler know if the given code should be expanded to this:

class Toggle {
  def happy: Receive = {
    return {
      case "How are you?" =>
        someFunction() // <-- The returned object of this function call is dismissed
    }
  }
}

Or this:

class Toggle {
  def happy: Receive = { // <-- Starting point of happy's body
    case "How are you?" =>
      someFunction() // <-- The Receive object is returned by this method
  }
}

Or more importantly, how am I supposed to know which expansion will happen? And I thought modern programming languages are supposed to be more readable!!!

Upvotes: 3

Views: 134

Answers (1)

Brian McCutchon
Brian McCutchon

Reputation: 8584

happy and sad are of type Function1

No, they are methods that return Receive, which is a type alias for PartialFunction[Any, Unit]. The type annotation you gave shows this return type. Partial functions can use the same lambda syntax as Function1 (and other SAM types), in which case they are distinguished only by context (in this case, your type annotation).

receive is supposed to return a PartialFunction (not being one)

You're confused on terminology. receive is a method, not a PartialFunction, as you declared it with def. It returns a PartialFunction[Any, Unit] (a.k.a Receive). This should also clarify your confusion over happy and sad.

This is a reasonable source of confusion, as the syntax is so concise as to make it look like receive and the PartialFunction it returns are one thing. It might help to realize that your code could be expanded to this (though I wouldn't advise it in practice):

class Toggle extends Actor {
  def happy: Receive = {
    return {
      case "How are you?" =>
        sender ! "happy"
        context become sad
    }
  }
  def sad: Receive = {
    return {
      case "How are you?" =>
        sender ! "sad"
        context become happy
    }
  }
  def receive = {
    return happy
  }
}

Per your update, the first expansion is correct. The second one doesn't make sense, as method bodies can't start with case, which should clue you in that you're dealing with a lambda. To sum up: if a block starts with lambda arguments (e.g. x =>), or it starts with case and isn't a match block, it's a lambda.

Upvotes: 6

Related Questions