👹

Akkaのコードに出てくる「!」の正体

2022/03/26に公開

皆さんこんにちは、株式会社プラハのエンジニアの粟田です。
最近リアクティブアーキテクチャにお熱なので、今日はAkkaの入門記事を書こうと思います。

対象となる読者

  • 最近Akkaを勉強し始めた
  • Scalaもそんなに読んだり書いたりしたことがない

今回解説するコード

以下のようなアクターが定義されているとします。

import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}

object TestActor {
  def apply(): Behavior[Command] =
    Behaviors.setup(context => new TestActor(context))

  sealed trait Command

  final case class Greet(message: String, replyTo: ActorRef[Respond]) extends Command
  final case class Respond(message: String)
}

class TestActor(context: ActorContext[TestActor.Command])
  extends AbstractBehavior[TestActor.Command](context) {
  import TestActor._

  override def onMessage(msg: Command): Behavior[Command] =
    msg match {
      case Greet(message, replyTo) =>
        replyTo ! Respond(message)
        this
    }
}

こちらのアクターを使ってテストを書いてみます。

import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
import org.scalatest.wordspec.AnyWordSpecLike

class TestActorSpec
  extends ScalaTestWithActorTestKit with AnyWordSpecLike  {
  import TestActor._

  "Test Actor" must {
    "メッセージを受信すると、そのまま返す" in {
      val probe = createTestProbe[Respond]()
      val testActor = spawn(TestActor())
      testActor ! Greet("hogehoge", probe.ref)
      val response = probe.receiveMessage()
      response.message should be("hogehoge")
    }
  }
}

皆さん、上記のテストコードを読んだときに、この行ってすぐに理解できましたか?
僕が最初に理解できたのは、「あ〜これでアクターにメッセージを送ってるんだな〜」というところまででした。

testActor ! Greet("hogehoge", probe.ref)

この記事では、この行が一体何をしているのか、どういう仕組みで動くのかを解説します。

! の正体

早速「!」の正体を説明すると、これは単なる「メソッド」です。

でもメソッドなら

testActor.!(Greet("hogehoge", probe.ref))

こういう呼び方するんじゃないの?と思いますよね。
(僕は思いました)

! の定義を見に行ってみる

IntelliJを使っている方はCmd+bでジャンプしてみてください。
以下のような定義に移動したかと思います。

object ActorRef {

  implicit final class ActorRefOps[-T](val ref: ActorRef[T]) extends AnyVal {

    /**
     * Send a message to the Actor referenced by this ActorRef using *at-most-once*
     * messaging semantics.
     */
    def !(msg: T): Unit = ref.tell(msg)
  }
}

ちゃんと ! がメソッドとして定義されています!

なぜ特殊な呼び方ができるのか

これはScalaがそういう書き方ができる言語仕様になっているからです。
Scalaを書ける方は既に知っているかもしれないですが、僕は最近まで知りませんでした!

scalaでは以下の呼び方は全て同じような動きをします

// いわゆる一般的な書き方
testActor.!(Greet("hogehoge", probe.ref))
// . を省略できる
testActor !(Greet("hogehoge", probe.ref))
// 引数の()を省略できる
testActor ! Greet("hogehoge", probe.ref)

まとめ

今回は僕と同じようにAkkaもScalaも最近勉強始めました!という方に何か参考になれば良いなと思って記事を書きました。

また、ただいま社外の人と一緒にAkka実践バイブルの輪読会を開催中です!
これを機にAkkaの勉強を始めてみようかな?と思っている方は、ぜひ輪読会に参加頂ければと思います!
もし興味がある方はこの記事にコメント、DMお待ちしています!

GitHubで編集を提案
アガルートテクノロジーズ/PrAha

Discussion