Raphael Roth
Raphael Roth

Reputation: 27373

Pattern matching in a scala method without arguments

I recently looked into scala multithreading tutorials and stumbled on a method which is implemented in an Actor:

class MyActor extends Actor
  def receive = {
    case "test" => println("received test")
    case _ =>      println("received unknown message")
  }
}

Although I think I know what the method does, I can't figure out how to use such a method (at it does not take any arguments).

Althouh the example is about Akka, I think this is unrelated to Akka

Upvotes: 2

Views: 352

Answers (2)

chengpohi
chengpohi

Reputation: 14217

As @Fatih Donmez said, it's a Parital Funciton, this defined certain values of certain types, usage:

List("hello", "test").foreach {
    case "test" => println("received test")
    case _ =>      println("received unknown message")
}

(0 to 20) collect {
    case i if i % 2 == 0 => i
}

(0 to 20) map {
    case i if i % 2 == 0 => i * i
    case j => j
}

also you can use it directly, like:

val f1: PartialFunction[Int, Unit] = {
   case 1 => println("I am 1")
   case t => println(s"I am $t")
}

f1(1)

> I am 1

f1(2)

> I am 2

orElse Example:

val f1: PartialFunction[Int, Int] = {
   case i if i % 2 == 0 => i * i
}

val f2: PartialFunction[Int, Int] = {
   case i if i % 2 != 0 => i+1
}

val f = f1 orElse f2

f(1)
> 2

(0 to 10) map f
> scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 4, 16, 6, 36, 8, 64, 10, 100)

andThen Example:

val f1: PartialFunction[Int, Int] = {
   case i if i % 2 == 0 => i * i
   case j => j
}

val f2: PartialFunction[Int, Int] = {
   case i if i % 2 != 0 => i * i
   case j => j + 1
}

val f = f1 andThen f2
f(2)
res20: Int = 5

(0 to 10) map f
> scala.collection.immutable.IndexedSeq[Int] = Vector(1, 1, 5, 9, 17, 25, 37, 49, 65, 81, 101)

compose Example:

val f1: PartialFunction[Int, Int] = {
   case i if i % 2 == 0 => i * i
   case j => j
}

val f2: PartialFunction[Int, Int] = {
   case i if i % 2 != 0 => i * i
   case j => j + 1
}


val f = f1 compose f2

f(2)
res18: Int = 3

(0 to 10) map f
res17: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 1, 3, 9, 5, 25, 7, 49, 9, 81, 11)

docs:

  1. https://twitter.github.io/scala_school/pattern-matching-and-functional-composition.html
  2. https://twitter.github.io/scala_school/pattern-matching-and-functional-composition.html

Upvotes: 1

Fatih Donmez
Fatih Donmez

Reputation: 4347

Actually receive returns partial function. When you check the source code of Actor you'll see that;

type Receive = PartialFunction[Any, Unit]

So it means with overriding receive you're providing a partial function which takes any parameter and return unit. At some point later, if your actor gets a message Akka itself will use this partial function to do pattern match over the incoming message.

-- Edit --

For more concrete example, let say you created a similar object;

object Obj {
  def receive: PartialFunction[Any, Unit] = {
    case "test" => println("test case")
    case 1 => println("int 1 case")
    case d: Double => println("double case $d")
    case _ => println("rest")
  }
}

Then you can call this partial function like regular method call;

scala> Obj.receive(1)
int 1 case
scala> Obj.receive("test")
test case

Actor case is specific, it's just that you don't call the receive directly, you provide it and Akka itself calls it if the messages arrive;

protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = receive.applyOrElse(msg, unhandled)

Of course this call depends on lots of parameters like the topology you use,mailbox and etc.

Upvotes: 7

Related Questions