Ponylangで型付きアクター生活[7] capability subtyping, arrow type編

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

はろぽに〜₍₍ (ง´・_・`)ว ⁾⁾ 今回で最終回です。

Capability subtyping

reference capabilityが付いている時のサブタイピング関係について見ていきます。

Simple substitution

まずは^!などが無い場合について考えます。

サブタイピング関係 説明
iso <: trn isoはread/writeユニークで、trnはwriteユニークなので、isoの方が制約が強いです。そのためisoはtrnのサブタイプです。
trn <: ref trnはmutableでwriteユニークです。refはmutableなので、trnの方が制約が強いです。そのためtrnはisoのサブタイプです。
trn <: val trnではwriteが可能ですが、writeする権利を諦めればglobal immutableになるので、trnはvalのサブタイプとなります。(例1)
ref <: box refは他のアクターではread/writeされないことを保証します。boxは他のアクターではwriteされないことを保証します。そのためrefはboxのサブタイプです。
val <: box valはどのアクターもwriteしないことを保証します。boxは自分のアクター以外でwriteされないことを保証します。そのためvalはboxのサブタイプです。
box <: tag boxは他のアクターでwriteされないことを保証します。tagは全く何も保証しないので、boxはtagのサブタイプです

例1:

   var s: String val = recover trn String().append("test") end

また上記の関係では全て推移律がなりたちます。つまりiso <: trn, trn <: refなのでiso <: ref となります。

Aliased substitution

次はiso!のような型について考えます。iso!はisoをconsumeせずにaliasingした場合に出てきます。

サブタイピング関係 説明
iso! <: tag isoと違い、iso!はtagのサブタイプです。すでにisoによってread/writeユニークな参照があるので、一切のread/writeが許されません。そのためiso!はtagのサブタイプになります。
trn! <: box writeユニークなtrnの参照が既に存在しているので、trn!はrefのサブタイプではありません。boxであればwriteユニークは守られますのでtrn!はboxのサブタイプだといえます。
ref! <: ref refは他のアクターに渡さなければreadもwriteも可能なのでref!もrefと同じように扱うことが出来ます。
val! <: val valは変更されないことを保証しており、readは自由にできるのでval!もvalと同じように扱うことが出来ます。
box! <: box boxもreadする参照が増える分には構わないので同様にboxと同じように扱うことが出来ます。
tag! <: tag tagは何も保証されていないのでtag!もtagと同じように扱うことが出来ます。

Ephemeral substitution

最後にiso^のような型について考えます。iso^isoな参照をconsumeした際に出てきます。

サブタイピング関係 説明
iso^ <: iso isoな参照はconsumeされているのでiso!と違いiso^はisoのサブタイプとして扱うことが出来ます。
trn^ <: trn trn^もiso^と同様の理由でtrnのサブタイプです。
ref^ <: ref^, ref^ <: ref, ref <: ref^ ref^とrefは完全に交換可能に扱うことが出来ます。
val^, box^, tag^ refと同じように完全に交換可能です。

ref^, val^, box^, tag^のような型は^無しの型と交換可能なので本来不要なはずですが、reference capability recoveryやgenericsについて考えるときに必要になるようです。

Arrow types

今まではoriginのreference capabilitiesがわかっていることを前提に説明を行ってきましたが、 originのreference capabilitiesが未定の場合もあります。その場合は(未定のorigin ×field)のreference capabilitiesを見て適切な型を自動で決めてくれるviewpoint adapted typeを使う必要があります。 viewpoint adapted typeはPonyでは arrow type と呼ばれています。

Using this-> as a viewpoint

boxなレシーバーで読めるメソッドはrefやvalなレシーバーからも呼ぶことが出来ます(ref, val <: boxなので)

下記の例を考えてみましょう。

class Wombat
  var _friend: Wombat ref

  fun friend(): this->Wombat => _friend

friendメソッドの返り値のreference capabilityはboxレシーバーから呼ぶとboxになります。

refレシーバーから呼ぶとref, valレシーバーから呼ぶとvalのように、全てにおいてboxを返すのではなくレシーバーによって柔軟に型を決めて欲しいときがあります。それを実現するのがarrow typeです。this -> Wombatのように書くと "レシーバーからみた Wombat ref"が返ってきます。

Using a type parameter as a viewpoint

genericsをまだやっていないので詳細はわかりませんが

class ListValues[A, N: ListNode[A] box] is Iterator[N->A]

のように書くとNから見たAのreference capabilitiesを持つAが帰ってくるようなIteratorを指定することが出来るらしいです。 (Nはboxと指定されていますが、サブタイピング関係から実際にはvalrefになる可能性があります。)

Using box-> as a viewpoint

上で少し解説しましたがgenericsにおいて、未定だけどとりあえずreadできる権限がある型はboxと指定することができます。

標準ライブラリの例を紹介します。

interface Comparable[A: Comparable[A] box]
  fun eq(that: box->A): Bool => this is that
  fun ne(that: box->A): Bool => not eq(that)

Comparable[A]の中でeq, neが定義されています。eq, neAをreadできる何かという意味でbox -> Aと指定されています。 実際にはどのようなreference capabilityでも構いませんがreadだけ出来ることを保障したい場合はこのように指定することができます。

終わりに

ドキュメントはまだ続くのですが、ここからは空白ページが目立つようになるので一旦お休みしようと思います。(genericsとかの解説がまだ無いので色々機能が使いこなせていなさそうです) 色々書きましたが特に自分自身がponyに詳しいわけでもなく公式のドキュメントを見て個人が勉強した記録みたいな感じになっていましたが、何かの役に立てばと思います₍₍ (ง´・_・`)ว ⁾⁾