👹
Akkaのコードに出てくる「!」の正体
皆さんこんにちは、株式会社プラハのエンジニアの粟田です。
最近リアクティブアーキテクチャにお熱なので、今日は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で編集を提案
Discussion