近況 2021-02-28

今月の活動 (ミローネ言語、DAPサンプル実装を作った、など)

ミローネ言語

https://github.com/vain0x/milone-lang

(リリース……)

ML: newtype バリアントを剥がす最適化

https://github.com/vain0x/milone-lang/commit/6946e433a006fcc95c48242f03ac4605d7eebaf2

次のように1つのバリアントしか持たない判別共用体は newtype と呼ばれている。

type UserId = UserId of int

UserId を他の整数と型レベルで区別することで誤りを防げる。 逆にいうと型検査が終わったら UserId と int を区別する理由はない。 そのため UserId を使っている部分を int を使うように変換してしまっていい。

    let (UserId n) = UserId 1
// =>
    let n = 1

これにより値を包んで剥がす無駄な操作を省略できる。 さらに単相化において K<UserId>K<int> の2つに分かれなくなるので嬉しい。

実際、ミローネ言語のコンパイラでもこの種の newtype をいくつか使っている。 例えば変数の識別子と関数の識別子を分けている。 そのせいでマップの実装が多く複製されていた。 この最適化を導入したことで、セルフコンパイルで生成されるCのコードが1万行ぐらい減った。

注意点が一つあって、再帰的に使われている newtype は剥がせない。 例えば次の Ty 型は newtype だが、再帰的なので剥がすことはできない。

    // 型は型コンストラクタと型引数のペア
    type Ty = Ty of Tk * Ty list

相互再帰の可能性もある。再帰性の判定は閉路検出 (深さ優先探索) のアルゴリズムを使った。 それぞれの型定義から出発して、再帰的にそれが含んでいる他の型を訪問していく。 処理中の型定義に到達したら、その型は再帰的に使われている。 到達しなかったら再帰的に使われていない (剥がせる)。

DAPのサンプル実装

https://github.com/vain0x/debug-adapter-examples/tree/main/adapter-minimal

デバッガの実装を言語側と開発ツール (エディタ) 側で分離するためにDAPというプロトコルがある。 LSPのデバッガ版のようなもの。

日本語の情報が少ない気がしたので、サンプル実装を作った。 とはいえ実装だけみても分かりやすくはないから、後で記事も書く予定。

どの場所で公開するか迷っていた。おそらくこのブログに書く。

関連記事