Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AltJS を作るなら型変換を入れた方がいい

cannorin
September 24, 2022

AltJS を作るなら型変換を入れた方がいい

情報科学若手の会で発表したスライドです.

cannorin

September 24, 2022
Tweet

More Decks by cannorin

Other Decks in Programming

Transcript

  1. AltJS を作るなら
    多分型変換を入れた方がいい
    cannorin

    View Slide

  2. 2
    関数型プログラム言語や型システムについての
    マニアックな知識が必要な部分があります
    そのようなスライドにはもくもくマークを付けました→
    分からない場合は聞き流して大丈夫なように頑張ります 

    View Slide

  3. 3
    最強の AltJS を作りたい
    ですよね?

    View Slide

  4. 4
    最強の AltJS とは…
    ・軽量構文!
    ・静的型付き!!
    ・関数型プログラミング!!!
    ・ JS の資産がそのまま使える!!!!
    これらだけ
    なら簡単!
    どうやれば????

    View Slide

  5. 5
    JS の資産を活用
    ・ Definitely Typed というものがある
    https://github.com/DefinitelyTyped/DefinitelyTyped
    npm パッケージの TypeScript 用型定義 (.d.ts) がいっぱいある
    → .d.ts から AltJS 用のバインディングを
      自動生成したい!!

    View Slide

  6. 6
    ここで自己紹介
    .d.ts から OCaml 系 AltJS (js_of_ocaml, ReScript) 用の
    バインディングを生成するツールを作っています
    他には AltJS コンパイラの中身をいじったり
    OCaml や F# のツール・ライブラリに contribute したり

    View Slide

  7. 7
    TypeScript の特徴
    ・クラスベース OOP
    ➾ 関数型プログラミングとは相性悪め!(今回はこれの話)
    ・構造的部分型付け
    ➾ 多くの TS プログラマは名前的部分型付けのつもりで
      書いているので,あまり気にしなくて OK かも
    ・充実した型操作
    ➾ Turing 完全になっちゃうので,ある程度以上は諦める

    View Slide

  8. 8
    関数型 AltJS に変換?
    ・クラスベース OOP
    → 関数型プログラミングとは相性悪め!(今回はこれの話)
    ・構造的部分型付け
    → 多くの TS プログラマは名前的部分型付けのつもりで
      書いているので,あまり気にしなくて OK かも
    ・充実した型操作
    → Turing 完全になっちゃうので,ある程度以上は諦める

    View Slide

  9. 9
    クラスベース OOP の変換
    ・ OOP をそのまま入れられるなら OK
    例 : F# ベースの AltJS: Fable (C# との連携用に元々入ってる )
    ・そうでない場合,以下の要素が問題になる
    ①メソッドのオーバーロード
    (関数のオーバーロードは関数型と相性悪い)
    ②クラス・インターフェースの継承
    (何らかの方法で部分型付けをやる必要あり)

    View Slide

  10. 10
    ① オーバーロード
    以下のような .d.ts を考えよう

    View Slide

  11. 11
    ① オーバーロード
    Hindley-Milner 型推論にオーバーロードを加えると決定不能になる [1]
    → 決定可能になるようにする or 諦める
    決定可能になるようにする…
    → 型クラスを導入する(例 : PureScript )
    → 制限された形のオーバーロードのみ受け付けるようにする [2]
    (難しいので,やってる AltJS は多分ない)
    諦める…
    → 決定不能でもいいか~(例: Fable)
    → オーバーロードごとに関数をリネームする(例: js_of_ocaml, ReScript)
    型 推 論 で き な い か も し れ な い

    View Slide

  12. 12
    ① オーバーロード(型クラス)
    PureScript を例に…

    オーバーロードされた foo 用の型クラス
    Fooable を作る

    Number 版と String 版を別々に import して,
    それぞれ Fooable のインスタンスにする

    すると一個の foo に String を渡しても
    Number を渡しても使えるようになる
    → そもそも言語に型クラスを実装するのが超大変
    → 自動生成はそんなに大変ではない
    ( 1 個のメソッドに対して 1 個の型クラスを作る必要があるが…)
    → ユーザとしては使いやすい
    (が,型クラス自体が難しい…)

    View Slide

  13. 13
    ① オーバーロード(リネーム)
    ReScript を例に…

    Number と String に対してそれぞれ
    fooNumber, fooString を external で定義
    (ただし,両者とも同じ foo にコンパイルさせる)

    使う際は,引数の型に応じて
    fooNumber か fooString を使い分ける
    → 言語側の実装はとても楽
    ( FFI 対象の名前を指定できるようにするだけ)
    → 自動生成もリネームするだけなので楽
    (リネーム時に名前衝突しないよう気を配る必要あり)
    → ユーザとしては少し使いづらい

    View Slide

  14. 14
    ① オーバーロード
    型クラスは言語機能としてかなり“重たい”
    ・例えば,存在型と組み合わせると型推論が決定不能になる [3]
    これに関しては諦めたほうが楽だと思う…
    ・型クラスで決定不能になるくらいなら最初から決定不能にしても同じ
    ・リネームはエディタの補完で何とかなる範囲なので,
     リネームで頑張りたい

    View Slide

  15. 15
    ② 継承
    以下のような .d.ts を考えよう

    View Slide

  16. 16
    ② 継承
    素朴にやると大変ヤバい

    継承したものも含めてメソッド全部追加しちゃお
    O(n2) で
    コード増加
    ( n: 型の数)

    View Slide

  17. 17
    ② 継承
    素朴にやると大変ヤバい

    継承元の型に変換する関数を足そう
    d->D.to_B
    ->B.to_A
    ->A.foo(42.0)

    View Slide

  18. 18
    ② 継承
    素朴にやると大変ヤバい

    継承元の型には全部一発で変換できるようにするか…
    O(n2) で
    コード増加
    ( n: 型の数)

    View Slide

  19. 19
    ② 継承
    Hindley-Milner 型推論に部分型付けを加えると実用上しんどい [4]
    → もっと軽量な仕組みで継承を再現したい
    われわれが求める要件としては,
    1. コード増加が O(n2) とかにならないようにしたい
    2. 継承元の型には全て一発で変換できるようにしたい(例: D から A )
    3. 外部 (TypeScript) の世界から来た型にのみ上記ができれば十分
    → このような型システムの研究はあまりされていないように見える
    (近いことをやっている論文 :[5] )

    View Slide

  20. 20
    ② 継承 : でもファンクタ使えばいいじゃん?
    → 継承時の型変数の数の変化が問題になる

    C では型変数 ’ a が増えてしまっている
    → A.Impl をそのまま include できない

    これを解消するには,’ a もファンクタ
    の引数として取るしかない
    → C を異なる ‘ a で使うたびに
    ファンクタを適用しないといけなくなる

    さすがにそれでは使いづらいのではないか
    ( AltJS としては OCaml ユーザだけでなく,
    既存の JS ユーザも取り込みたいところ)

    View Slide

  21. 21
    ② 継承 : でも row polymorphism 使えばいいじゃん?
    → オーバーロードされたメソッドが問題になる

    c は a も b も継承していることにしたい

    row polymorphism で部分型になるには,
    名前と型が一致してないといけない

    しかし,同じ名前で複数のフィールドを
    レコード c に定義することはできない

    c でどちらかをリネームすると.
    row polymorphism が壊れてしまう

    a か b の foo を事前にリネームしておくことも
    できない(もし別ライブラリで定義されてたら…)

    View Slide

  22. 22
    ② 継承 : row polymorphism + phantom types
    ・ row polymorphism をそのまま使うのではなく,
     「継承している型の列」に対して使う
    ・「型の列」は phantom type として保持
    ・例 : B は A を継承しているので #B と #A を持つ
    ・この手法ならわれわれの求める要件を満たせる
      - コード増加は O(n) に収まる
      - 明示的に書く必要はあるが, D→A の変換は一発
    → これをきちんと言語機能として組み込めば,
      型推論を決定可能に保ちつつ, TypeScript の継承
      をうまく翻訳できるのではないだろうか?

    View Slide

  23. 23
    ② 継承
    なんにせよ,
    JavaScript/TypeScript の世界からやってきた型を,
    継承関係に従って簡単に変換できるような専用の仕組み
    が,最強の AltJS を作るためには必要
    そして,それは最強の AltJS の
    「最強性」を失うことなしに実現できそうな気がする

    View Slide

  24. 24
    参考文献
    [1] Dennis M. Volpano and Geoffrey S. Smith. On the complexity of ML typability with overloading.
    https://ecommons.cornell.edu/handle/1813/7050
    [2] Geoffrey S. Smith. Polymorphic Type Inference for Languages with Overloading and Subtyping.
    https://ecommons.cornell.edu/handle/1813/7070
    [3] Sulzmann, Martin & Schrijvers, Tom & Stuckey, Peter. Principal Type Inference for GHC-Style
    Multi-parameter Type Classes.
    https://www.researchgate.net/publication/221323263
    [1] Dennis M. Volpano and Geoffrey S. Smith. On the complexity of ML typability with overloading.
    https://ecommons.cornell.edu/handle/1813/7050
    [4] François Pottier. Type Inference in the Presence of Subtyping: from Theory to Practice.
    https://hal.inria.fr/inria-00073205
    [5] Fluet, Matthew and Pucella, Riccardo. Phantom Types and Subtyping.
    https://arxiv.org/abs/cs/0403034

    View Slide