知らない言語を使ってみたい - pony 編 (Erlang,Scala(Akka)+Rust みたいな)

どこか遠くへ行きたい

今年も沢山の知らない言語の情報が流れてきました。
今日はそれらの中から pony をピックアップし体験してみた。

# pony

[公式サイト : https://www.ponylang.io/](https://www.ponylang.io/)
[公式チュートリアル : https://tutorial.ponylang.io/](https://tutorial.ponylang.io/)
[リポジトリ : https://github.com/ponylang/ponyc](https://github.com/ponylang/ponyc)
[Stdlib ドキュメント : https://stdlib.ponylang.org/](https://stdlib.ponylang.org/)

> Pony is an open-source, object-oriented, actor-model, capabilities-secure, high-performance programming language.

|  |        | 
|-----------------:|------------------|
| 用途 | 汎用 | 
| 開始 | 2012/11 | 
| 最新バージョン | 0.25.0 ( 2018/10/13 )| 
| ライセンス | BSD 2-Clause |
| プラットフォーム | LLVM |
| 型 | 静的型付け | 
| パッケージ管理 | [pony-stable](https://github.com/ponylang/pony-stable) | 
| Githubスター | 3507 ( 2018/11/28 ) |

- オブジェクト指向
- アクターモデルによる並列処理
- コンパイル時に様々な問題を検出 ( capabilities-secure と呼ばれている )
    - 型安全
    - メモリ安全 ( Null 許さない )
    - 例外安全 ( 実行時例外なし )
    - データレースなし
    - デッドロックなし
- ハイパフォーマンス
    - ネイティブコード
    - C 呼び出し

Erlang, Scala + Rust みたいな感じだろか。

# インストール

・Windows 10 Pro 1803

[Github に丁寧な説明がある](https://github.com/ponylang/ponyc/blob/master/README.md#installation) ので、迷わず進める。
今回は、バイナリを落とした。

```powershell
$ ponyc.exe -v
# 0.25.0-232a0ab [release]
# compiled with: llvm 3.9.1 -- msvc-15-x64
# Defaults: pic=false ssl=openssl_0.9.0
```

動いた。

# エディタ支援

[種類は充実している](https://github.com/ponylang/ponyc/blob/master/README.md#editor-support)。が、ほとんどがシンタックスハイライトやコメント入力支援等のごく基本的な物だった。
オートコンプリートやリンタ、シンタックスエラー的な機能は無かった。
[Language Server](https://github.com/ponylang/pony-language-server) のプロジェクトもあったが、空っぽだった。

# サンプル体験

## hello world

https://github.com/ponylang/ponyc/blob/master/examples/helloworld/main.pony

```pony
actor Main
  new create(env: Env) =>
    env.out.print("Hello, world!")
```

```powershell
$ ponyc.exe .
# Building builtin -> C:\ponyc\ponyc-0.25.0-win64\packages\builtin
# Building . -> C:\sample_1204\pony
# Generating
#  Reachability
#  Selector painting
#  Data prototypes
#  Data types
#  Function prototypes
#  Functions
#  Descriptors
# Optimising
# Writing .\pony.obj
# Linking .\pony.exe
#    Creating library .\pony.lib and object .\pony.exp

$ .\pony.exe
# Hello, world!
```

動く。

## counter

https://github.com/ponylang/ponyc/blob/master/examples/counter/main.pony

```pony
use "collections"

actor Counter
  var _count: U32

  new create() =>
    _count = 0

  be increment() =>
    _count = _count + 1

  be get_and_reset(main: Main) =>
    main.display(_count)
    _count = 0

actor Main
  var _env: Env

  new create(env: Env) =>
    _env = env

    var count: U32 = try env.args(1)?.u32()? else 10 end
    var counter = Counter

    for i in Range[U32](0, count) do
      counter.increment()
    end

    counter.get_and_reset(this)

  be display(result: U32) =>
    _env.out.print(result.string())
```

- `var counter = Counter` でアクターが作られる
    - 普通のクラスのように簡単に使える
- `?` 付きは例外発生の可能性がある？
    - `try ~ else ~ end` で囲う必要がある？

## mailbox

https://github.com/ponylang/ponyc/blob/master/examples/mailbox/main.pony

`ping` と `pong` にログ出力をちょっと追加

```pony
use "collections"

actor Mailer
  be ping(receiver: Main, pass: U32) =>
    receiver.display("ping")
    for i in Range[U32](0, pass) do
      receiver.pong()
    end

actor Main
  var _env: Env
  var _size: U32 = 3
  var _pass: U32 = 0
  var _pongs: U64 = 0

  new create(env: Env) =>
    _env = env

    try
      parse_args()?
      start_messaging()
    else
      usage()
    end

  be pong() =>
    _pongs = _pongs + 1
    display("pong" + _pongs.string())

  be display(result: String) =>
    _env.out.print(result)

  fun ref start_messaging() =>
    for i in Range[U32](0, _size) do
      Mailer.ping(this, _pass)
    end

  fun ref parse_args() ? =>
    _size = _env.args(1)?.u32()?
    _pass = _env.args(2)?.u32()?

  fun ref usage() =>
    _env.out.print(
      """
      mailbox OPTIONS
        N   number of sending actors
        M   number of messages to pass from each sender to the receiver
      """
      )
```

```powershell
$ ponyc
# Building builtin -> C:\ponyc\ponyc-0.25.0-win64\packages\builtin
# Building . -> C:\sample_1204\pony
# Building collections -> C:\ponyc\ponyc-0.25.0-win64\packages\collections
# Building ponytest -> C:\ponyc\ponyc-0.25.0-win64\packages\ponytest
# Building time -> C:\ponyc\ponyc-0.25.0-win64\packages\time
# Generating
#  Reachability
#  Selector painting
#  Data prototypes
#  Data types
#  Function prototypes
#  Functions
#  Descriptors
# Optimising
# Writing .\pony.obj
# Linking .\pony.exe

$ .\pony.exe 3 3
# ping
# ping
# ping
# pong1
# pong2
# pong3
# pong4
# pong5
# pong6
# pong7
# pong8
# pong9
```

- アクターには `be` と `fun` がある
    - いわゆるメソッドが `fun`
        - 同期実行
    - `be` は [behaviour](https://tutorial.ponylang.io/types/actors.html)
        - 非同期実行
        - 戻り値は `None`
- `fun` に `ref` がついている
    - [capability](https://tutorial.ponylang.io/capabilities/reference-capabilities.html) を指定している
    - `ref` capability は外すと以下エラーが発生
        - `main.pony:39:11: cannot write to a field in a box function. If you are trying to change state in a function use fun ref
    _size = _env.args(1)?.u32()?`
        - `main.pony:40:11: cannot write to a field in a box function. If you are trying to change state in a function use fun ref
    _pass = _env.args(2)?.u32()?`
        - デフォルトの capability が `box` (read only) なのでこうなると

# capability 

https://tutorial.ponylang.io/capabilities/

多分重要なやつ。

基本的な考え方は、

- アクターはシングルスレッド
    - なので、その中の処理でデータ更新が問題になることはない
- Immutable なデータはアクター間で共有されても問題ない
- Mutable なデータを複数アクターで共有し、更新し合うのが問題を引き起こす
    - 参照がアクター問わず一つしか無い事 ( isolated と呼ばれる ) が保証されれば、問題は起こらない

ということで、**Immutable なデータか isolated なデータの共有だけ行えば並行処理の問題は起こらない** というのが方針らしい。

なるほど、アクター間のメッセージのコピーにかかるオーバーヘッドは、適切にメモリアクセスを管理された Shared memory を使うことでなくすことが出来るのか。少し分かってきたぞ。

## reference capability

https://tutorial.ponylang.io/capabilities/reference-capabilities.html

ということで、変数に修飾子として以下を付けることで read/write を制御する。

| 修飾子  | 参照 (自身) | 他の参照 (自アクター) | 他の参照 (他アクター) | 備考 | 
|-------:|:-------:|:-------:|:-------:|:-------|
| `iso` | read / write | × | × | | |
| `trn` | read / write | read | × |  | 
| `ref` | read / write | read / write | × |  | 
| `val` | read | read | read | |
| `box` | read | read or read/write | read or × | 実態は `val` or `ref`? どちらでも read は保証しているので、この参照で read はできる | |
| `tag` | | | | 識別子。アイデンティティ比較に利用 | 

見ただけで、何となくは理解したのかな？と思って、実際に書いてみたけど全然書けなかった…
コンパイルエラーしか見てない。

capability の移動やサブタイピング、ジェネリクスなどなど… 考えないといけないことが沢山ある。
今はあまり深みにはまらないようにしよう。

# subtyping

https://tutorial.ponylang.io/types/traits-and-interfaces.html

↓ trait 

```pony
trait Red
  fun color(): String => "red!!!"

trait Rectangle
  fun area(): String => "wide!!!"
```

↓ 普通に実装

```pony
class Cherry is Red
  fun say(): String => color()

var cerry = Cherry
env.out.print(cerry.say())
// red!!!
```

↓ 複数実装

```pony
class Kanikama is (Red & Rectangle)
  fun say(): String => color() + area()

var kanikama = Kanikama
env.out.print(kanikama.say())
// red!!!wide!!!

// 組み合わせにエイリアスが付けられる (Complex types と言うらしい)
type RedRect is (Red & Rectangle)

class Kanikama is RedRect 
```

Structural subtyping

```pony
interface Circle
  fun bound(): String

class Ball
  fun bound(): String => "boyoyon!!!"

var circle: Circle = Ball
env.out.print(circle.bound())
// boyoyon!!!
```

# 表現力

https://tutorial.ponylang.io/types/type-expressions.html

```pony
// touple
var x = (I64(1), "one", true)
env.out.print(x._2)
// one


// Union
var y: (String | I64) = if true then 10 else "20" end
match y
| let s: String => env.out.print(s)
| let n: I64 => env.out.print(n.string())
end
// 10


// Intersections
interface box Hashable
  fun hash(): USize

var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5)] // 構造的にに全部 Hashable の subtype
for v in z.values() do
  if v.string() != "1"  then
    env.out.print(v.hash().string())
  end
end
// 9912775131124690402
// 3620018180187439284
```

凄い

**[追記]** もしやと思って `z: Array[(Hashable & Stringable)]` の型指定を外しても動いた。
型推論で `z` は `(I64 | String | F64)` と判断されて、それらすべてが `hash(): USize` を持ってるので行けるっぽい。

ちなみに、配列に `true: Bool` を追加するとそれぞれ違うエラーが出る。

```
# var z = [I64(1); "2"; F64(3.5); true] の場合

Error:
C:\sample_1204\pony\main.pony:64:24: couldn't find 'hash' in 'Bool'
        env.out.print(v.hash().string())
                       ^
Error:
C:\sample_1204\pony\main.pony:64:24: couldn't find hash in Bool val
        env.out.print(v.hash().string())



# var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5); true] の場合

Error:
C:\sample_1204\pony\main.pony:63:69: array element not a subtype of specified array type
    var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5); true]
                                                                    ^
    Info:
    C:\sample_1204\pony\main.pony:63:28: array type: (Hashable box & Stringable box)
        var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5); true]
                               ^
    C:\sample_1204\pony\main.pony:63:69: element type: Bool val
        var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5); true]
                                                                        ^
    C:\sample_1204\pony\main.pony:63:69: Bool val is not a subtype of Hashable box: it has no method 'hash'
        var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5); true]
                                                                        ^
    C:\sample_1204\pony\main.pony:63:69: Bool val is not a subtype of every element of (Hashable box & Stringable box)
        var z: Array[(Hashable & Stringable)] = [I64(1); "2"; F64(3.5); true]
```

エラーも優しかった

# パターンマッチ

個人的に重要なので気になる。

```pony
fun echo(x: (U32 | String | Foo | (U32, String) | None)): String =>
  match x
  | 2 => "two"                                  // 値にマッチ
  | 3 => "three"
  | Foo(5) => "foo 5"                           // Equatable なインスタンスにマッチ
  | None => "none"                              // 型でマッチ
  | let u: U32 => "int " + u.string()           // 値をキャプチャして利用可能
  | (1, "bob") => "bob is one"                  // タプルのマッチ
  | (2, let name: String) => name + " is two"   // タプルで値をキャプチャ
  | let s: String if s != "ignored" => s        // ガード
  else                                          // 何もなければ else に
    "no match"
  end

echo(2)             // "two"
echo(Foo(5))        // "foo 5"
echo(None)          // "none"
echo(5)             // "int 5"
echo((2, "marley")) // "marley is two"
echo("hoge")        // "hoge"
echo("ignored")     // "no match"


// Equatable なクラス
class Foo
  var _x: U32
  new create(x: U32) =>
    _x = x
  fun eq(that: Foo): Bool =>
    _x == that._x
```

とても直感的で、理不尽にコンパイラに怒られない。

これに capability も条件として付けられるが、コンパイラを納得させることができなかった。


# 感想

- アクターがカジュアルに使えた
- 表現力高い気がした
- 安全性をどう担保しているのかも少し見えた ( しかし、コードで実感するまでは至れなかった )
- とはいえ、capability と高い表現力、型、アクターが全部絡んだら一筋縄ではいかなそう
- **『使ってみた』とか言って1日で理解できるレベルのもんじゃなかった**

|  |        | 
|-----------------:|------------------|
| Production Ready (独断) | ★★★★☆ |
| ドキュメント充実 | ★★★☆☆ |
| 公式以外の情報 | ★★☆☆☆ |

凄くおもしろそうだったので、機会があったら使ってみたいと思いました。
もっと流行っても良い気がする。

# 参考

https://www.slideshare.net/matsu_chara/ponylang-88081553

# あとがき

※ この記事は個人の見解であり、所属する組織を代表するものではありません。