読者です 読者をやめる 読者になる 読者になる

Ponylangで型付きアクター生活[5] 続CapabiltiesとAliasing編

注意 ここにある内容はpony2.x時代にドキュメントに書かれていた物なので、もう古い可能性が高いです。最新の情報は公式ドキュメントを参照してください。

はろぽにー₍₍ (ง´・_・`)ว ⁾⁾

前回でCapabilitesでましたけど、じゃあコンパイルが通ると何が保証されているのかはぼんやりしたままでした。

ということで今回はそのへんをやります。 本家だとこの辺です。→ http://tutorial.ponylang.org/capabilities/guarantees/

Guarantees

reference-capabilities の意味は前回だいたい紹介しましたが、他のアクターに渡せるとか何がmutableで何がimmutableなのかを把握しておくことは安全な並行処理プログラミングを行うために非常に重要です。

Mutable reference capabilities

mutableなreference capabilitiesはiso, trn, refの三つです。 それぞれについて何が保証されるか見ていきます。

reference capability その変数自身 自アクター の他の変数 他アクターの変数
iso read/write ○ read/write × read/write ×
trn read/write ○ read ○ / write × read/write ×
ref read/write ○ read ○ / write ○ read/write ×

以上の表のようにmutableなreference capabilitiesでは他アクターからその変数の値を読み取ることさえ出来ません。 自アクターが変数を書き換えてしまうのでそりゃそうですよね。

一方で自アクター内では微妙に違いがあります。それぞれの特長は以下のようになっています。

  • isoは、 read/writeユニー です。その変数をread/writeすることができる唯一の変数です。
  • trnは、 writeユニー です。その変数にwriteすることができる唯一の変数です。readは自アクターなら誰でもできます。
  • refは、 特にユニークではありません。その変数を書き換える他の変数がいるかもしれませんし、他の変数から値を読み取ることが出来ます。

他アクター内ではreadできないからデータ競合が起きないのは分かるけど、なんで自アクター内でもreadやwriteを制限するのか?という疑問については他アクターへ参照を渡すときを考えると分かりやすいです。例えばisoであれば、今持っている他アクターへisoな変数の参照を渡すと同時に自アクターで持っていた参照を無効にすれば、そのデータへのread/writeユニー性は保たれます。一方でrefだと一つのrefを向こうにしても他に参照がいるかもしれませんから、ちょっと向こうにしたところで結局、他のアクターへ安全に渡すことが出来ません。このように自アクター内でも制限を設けることでアクター同士のデータのやりとりの際にどういうデータなら渡しても安全か、を規定することができます。(詳しくは今後の章で解説され(ると思い)ます。)

Immutable reference capabilities

immutableなreference capabilitiesはval, box, の二つです。 それぞれについて何が保証されるか見ていきます。

reference capability その変数自身 自アクター の他の変数 他アクターの変数
val read ○ / write × read ○ / write × read ○ / write ×
box read ○/write × read ○ / write ○(注1) read ○(注1) / write ×

注1: 排他になる。(どちらか一方が○だったら、もう一方は×になる)

それぞれの特長は以下のようになっています。

  • valは、globally immutableです。誰も値を変更することが出来ないため、様々なアクターから安全にreadが可能です。
  • boxは、locally immutable です。boxな参照を通しては値を変更することはできません。しかし、他の自アクター内の変数が値を変更するかもしれません。また、boxな参照を持つデータがvalな場合は他のアクターから値をreadすることができます。

box がなんだか複雑ですが、以下の状況を思い浮かべると分かりやすいと思います。

actor Main
  new create(env: Env) =>
    var trn_s: String trn = recover String().append("test") end
    var box_s1: String box = trn_s
    
    var val_s: String val = "test"
    var box_s2: String box = val_s

trn_strnなので自アクター内ではwriteが可能です。一方で他のアクターからtrn_sbox_s1が参照しているデータをreadすることはできません。 val_svalなので自アクター内でのwriteは出来ません。一方で他のアクターからval_sbox_s2が参照しているデータをreadすることができます。

Opaque reference capabilities

最後の一種類はtagです。tagな参照はreadもwriteも出来ません。オブジェクトの同一性の判定には使うことが出来ます。

Aliasing

エイリアシングっていうと大したことは無いと思うかもしれませんが、Ponyのaliasingはちょっと考える必要があります。

aliasing and deny gurantees

ここでいうaliasingは例えば

fun test(a: Wombat iso) =>
  var b: Wombat iso = a // Not allowed!

のようなことを指しています。参照に別名がついているのでaliasingです。コードのコメントにも書いてありますが、単純に同じ型とreference capabilitiesを持つa,bに対して b = a を行うことができません。a, bともにisoな参照なのでaliasingするとread/writeユニークな保障が崩れてしまうからです。

bがtagなら以下のように代入を行うことが出来ます。

fun test(a: Wombat iso) =>
  var b: Wombat tag = a // Allowed!

tag はreadもwriteも出来ないので、isoをaliasingしてもread/writeユニークが保たれるからです。

aがtrnの場合、bがboxである必要があります。

fun test(a: Wombat trn) =>
  var b: Wombat box = a // Allowed!

bがisoだとaが参照を持っているのでダメです。bがvalだとaが変更できてしまうのでダメです。bがrefだとtrnのwriteユニークが満たされなくなるのでダメです。bがtagの場合はOKです。

変換を表にまとめると以下のようになります。

from to
iso tag
trn box, tag
ref ref, box, tag
val val, box, tag
box box, tag
tag tag

isotrnが自分自身に変換できないので、うっとなりますがそれ以外は大丈夫なのでなんとかなりそうです。 read/writeユニークなiso のaliasingは厳しい。writeユニークなtrnboxを通せば見れる。と頭に入れておきましょう。

aliasingはいつ行われるか?

aliasingは代入したとき、引数として渡した時に発生します。

ephemeral types

isoな値は代入することができないので、val a: Foo iso = Foo.createIso()のようなことをすることができません。 それは困るのでephemeral typeという型を用意します。ここでFoo.createIso()isoではなくiso^を返すようにすると、 受け取ってくれたら、自分はすぐに参照を破棄するよ〜といった意味になります。

返り値の型がiso^な関数は以下のように作ることが出来ます。

fun test(a: Wombat iso): Wombat iso^ =>
  consume a

consumeについては後ほど解説されますので、今は ^がついてたらisoで受け取っていいんだなと思っておきましょう。 trnに関してもtrn^で同じことが出来ます。

alias types

ephemeral typeと同じような理由でalias typeという概念があります。

fun test(a: A) =>
  var b: A! = a

のようにするとA!は「何でもいいからAから安全にaliasできる型」という意味になります。

終わり

Ponyでは同じ型(というかreference capabilities)同士で代入することすらできないことが判明しました。 これでは色々不便というか実用できるのか!という気分になりますが、次回に出てくるconsumeを学べば色々便利になるのでこうご期待です₍₍ (ง´・_・`)ว ⁾⁾