Ponylangで型付きアクター生活[2] TypeとActor編

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

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

ということで引き続きPonyの機能を見ていきます。

Type

本家だとこの編です。 http://tutorial.ponylang.org/types/static-vs-dynamic/

Ponyは静的型付け&null無しな言語です。 CやC++のコードを呼ぶと残念ながらその部分の型安全性の保障は無くなってしまいます。

Class

Ponyのクラスには継承はありません。代わりにtraitやgoのinterfaceのようなものがあります。

class Wombat
  let name: String
  var _hunger_level: U64

のように宣言します。Ponyでは大文字で始まるものは常に型を表します。 let/varはお察しの通り。_で始まるフィールドはプライベート(同じ型からしかアクセスできない)です。*1

U64のようにかなり厳密な数値型を用意しているのも特長だと感じました。

コンストラクタはnewさえつければ名前をつけることができるので以下の様な便利なコンストラクタを宣言することが出来ます。

class Wombat
  let name: String
  var _hunger_level: U64

  new create(name': String) =>
    name = name'
    _hunger_level = 0

  new hungry(name': String, hunger': U64) =>
    name = name'
    _hunger_level = hunger'

name'のような'は意味ありげですが、そうすることができる以上の意味は無いそうです。わりと便利そう。

functions

関数はfunというキーワードで宣言することが出来ます。(結果の型を省略するとNoneという型になります。*2

class Wombat
  var _hunger_level: U64

  new hungry(hunger': U64) =>
    _hunger_level = hunger'

  fun hunger(): U64 => _hunger_level

  fun ref set_hunger(to: U64 = 0): U64 => _hunger_level = to

set_hungerのようにfun refとするとメソッドレシーバがreference typeのときだけ(= オブジェクトがmutableなときだけ)呼び出すことが出来るメソッドを定義することができます。このようなレシーバのmutableやimmutableを指定するrefなどがreference capabilityと呼ばれる概念です。

ちなみにreference capabilityが指定されていないhunger メソッドではboxというreference capabilityが指定されたことになります。boxは「読みはするけど、書きはしない」といった参照を表します。

ちなみにrefの指定を忘れると

/Users/matsu_chara/Documents/sand/helopony/main.pony:9:53: cannot write to a field in a box function
  fun set_hunger(to: U64 = 0): U64 => _hunger_level = to

のように怒られます。カタアンゼ...キャパビリティセーフ!

Actor

actorはclassと似ていますがbehaviorという非同期メソッドを持っている点が異なります。 behaviorはbeというキーワードで宣言されます。またbehaviorを呼び出すと常にレシーバそのものをすぐに返却します。

下記がアクターの例です。

actor Aardvark
  let name: String
  var _hunger_level: U64 = 0

  new create(name': String) =>
    name = name'

  be eat(amount: U64) =>
    _hunger_level = _hunger_level - amount.min(_hunger_level)

Ponyのランタイムは独自のスケジューラを持っているのでたくさんbehaviorを呼び出すことが出来ます。スケジューラはデフォルトでCPUコアと同じスレッドを持っていて、その数だけ並行に処理が行われます。(ただしアクターモデルなので一つのアクターはSequentialに処理を行います。)

さきほど出てきたrefやboxのような型修飾子を使いこなせばcapabilities secure type systemの力を借りることができるので、安心して色々な処理を平行に行うことが出来るそうですね。₍₍ (ง´・_・`)ว ⁾⁾

ちなみに1アクターは256byteらしいのでたくさん作りまくれそうです。

Subtyping

Ponyでは宣言的部分型も構造的部分型もサポートされています!

宣言的部分型にはtraitを使います。Ponyのtraitは変数を持てません。以下のように使うことが出来ます。

trait Named
  fun name(): String => "Bob"

class Bob is Named

構造的部分型にはinterfaceを使います。これはgoで使われているものに近いものです。

interface HasName
  fun name(): String

Type Alias

Ponyのtype aliasはunion typeやintersection typeを表現できます。

primitive Red
primitive Blue
primitive Green

type Colour is (Red | Blue | Green)
interface HasName
  fun name(): String

interface HasAge
  fun age(): U32

interface HasAddress
  fun address(): String

type Person is (HasName & HasAge & HasAddress)

良さそうですね。

以下のように複雑な型を表現しながら型変数に制約を加える事も出来ます。

type Map[K: (Hashable box & Comparable[K] box), V] is HashMap[K, V, HashEq[K]]

この場合KHashable boxComparable[K] boxでなければなりません。boxのようなreference capabilityが指定されているのがPonyっぽくておもしろいですね。

終わり₍₍ (ง´・_・`)ว ⁾⁾

ということで、今回はここまでです。これいつまでやるのか謎ですがreference capabilityがなんなのか気になるのでそのへんまでは勉強したいです₍₍ (ง´・_・`)ว ⁾⁾

*1:関数やコンストラクタ、ビヘイビアがプライベートのときは同一パッケージ以外からのアクセスを防ぎます

*2:ScalaのUnitのようなプリミティブ