アクタ

アクタの書き方

状態を持つものはアクタです。クラスを一つ書き、フィールドに状態を並べます。メソッドがそのままメッセージハンドラで、ハンドラは次の状態を返します。

Actor subclass: #Cart fields: (items : Array[Str]).

Cart class >> new   = Cart fields: { items: #[] }.
Cart >> add: item   = self with: { items: items copyWith: item }.
Cart >> count       = self reply: items size.

self with: { ... } は、書いた欄だけを差し替えた新しい状態を返します。元の状態は変わりません。返事が要るハンドラは self reply: を使います。

値を書き換える方法はありません。状態が変わるのは、ハンドラが次の状態を返したときだけです。

送り方は二つ

c := Actor spawn: Cart new.

c tell add: '六角ボルト'.              "送るだけ。待たない"
(c ask count) forced printNl.          "返事を待つ"

tell は送って終わりです。askFuture を返し、forced でその場で待ちます。

ハンドラの中で待つと、そのアクタは返事が来るまで次のメッセージを取りません。 画面全体が止まるわけではありません。他のアクタは動き続け、イベントも落ちません。100ms かかる照会を三件投げたアクタは 104ms、207ms、320ms と順に処理し、その間も別のアクタは 3ms、70ms、136ms で動いていました。

メッセージは送った順に届きます。待っている間に届いたものは、返事のあとで順番どおりに処理されます。

待たない書き方

通信の返事を、自分宛のメッセージに変えられます。こう書くと、ハンドラはその場で終わります。

Screen >> load = (
  (Http get: 'orders.json') then: [:r | me ref tell loaded: r].
  self ).

Screen >> loaded: r = self with: { rows: ... }.

通信している間も画面は操作に答えます。WebSocket も同じ形にします。コールバックの中では me ref tell arrived: を投げるだけにします。

落ちたとき

try はありません。ハンドラの中でエラーが出ると、そのアクタは止まります。止まったことは、見ている側に届きます。

Boss >> start   = ( me monitor: worker. self ).
Boss >> down: d = self with: { log: log copyWith: d actor name , ' が落ちた' }.

monitor: は知らせを受け取ります。link: は自分も一緒に止まります。監督役を作りたいときは、down: を受け取って代わりを spawn: する普通のアクタを書きます。

止まるのはそのアクタだけです。他のアクタの状態も、画面の他の場所も、影響を受けません。

状態が変わるときはクラスを変える

become: で別のクラスになれます。閉じた扉と開いた扉のように、状態によって受け付けるメッセージが変わる場合に使います。

Door >> open = self become: (OpenDoor fields: {}).

何を保たなければならないかは、参照の型で決まります。

Actor spawn: で作った参照は ActorRef[Door] です。Door が受け付けるものを全部受け付け続けるので、遷移先もそれを全部持っていなければなりません。足りなければコンパイルエラーです。

Actor spawn:as: でプロトコルとして作った参照は、そのプロトコルの分しか受け付けません。この場合、遷移先はプロトコルの分だけ実装すれば済みます。それ以外のハンドラを持っていても、外からは届きません。

互いに待ち合ったとき

二つのアクタが互いの返事を待つと、どちらも永久に動きません。誰も落ちていないので監視も鳴りません。

そこで ask は、送る前に相手が誰を待っているかを辿ります。辿った先が自分に戻ってきたら、その場でエラーにします。

Pong#2 asked Ping#3, which is waiting on it — nobody can take a turn

見つかったものは必ず閉路です。ただし、この検査は同じスレッドの中だけです。 Web Worker の上のアクタと待ち合った場合は見つかりません。スレッドを跨いで互いに ask する形は避けてください。