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

A brief introduction to type inference

A brief introduction to type inference

We present a brief introduction to what "type inference" is and how it works, in 5 minutes! This talk will take place in VRCLT #2. Please visit their website https://vrc-lt.github.io/ for more information.

452d2be966ed20b894b1fa66ce44b1a1?s=128

cannorin

June 14, 2019
Tweet

Transcript

  1. 1 / 42 VRCLT #2 型推論を支える技術 cannorin

  2. 2 / 42 誰 • Twitter: @cannorin_vrc • Study: 数理論理学,プログラム言語の理論

    • Job: F# プログラマ(アルバイト)
  3. 3 / 42 不毛な型推論バトル • C#/Java の var みたいなやつでしょ •

    Python も型書かなくていいじゃん • そんな最近出てきた機能知らない
  4. 4 / 42 不毛な型推論バトル ←こわい • C#/Java の var みたいなやつでしょ

    → あいつらはクソザコ! • Python も型書かなくていいじゃん → 動的型付けと一緒にするな! • そんな最近出てきた機能知らない → 昔からあるし!
  5. 5 / 42 なぜ繰り返されるのか • 型推論とは一体なんなのか理解されていない • 型推論の仕組みが知られてない • わからないものは怖がられがち

  6. 6 / 42 なぜ繰り返されるのか • 型推論とは一体なんなのか理解されていない (動的型付けとの違いがわからない) • 型推論の仕組みが知られてない (

    C#/Java の var との違いがわからない) • わからないものは怖がられがち (一般にアカデミアの人間は怖がられがち)
  7. 7 / 42 → 今回の目標! • 型推論とは一体なんなのか説明する • 型推論の仕組みを簡単に解説する •

    わかったつもりになってもらう
  8. 8 / 42 1 型推論ってそもそも何? • 主に静的型付き言語をターゲットとする

  9. 9 / 42 1 型推論ってそもそも何? • 主に静的型付き言語をターゲットとする • 変数・関数の型がほとんど or

    全く書かれていない コードに
  10. 10 / 42 1 型推論ってそもそも何? • 主に静的型付き言語をターゲットとする • 変数・関数の型がほとんど or

    全く書かれていない コードに • もっとも汎用的な正しい型を当てはめる手法
  11. 11 / 42 1.1 要するに let-function add_two (x : int)

    : int = + x 2 let-function apply_twice (f : int → int) (x : int) : int = f (f x) let-value foo : int = apply_twice add_two 0 もともとはこういう言語
  12. 12 / 42 1.1 要するに let-function add_two (x : int)

    : int = + x 2 let-function apply_twice (f : int → int) (x : int) : int = f (f x) let-value foo : int = apply_twice add_two 0 プログラマが型を書かなくても
  13. 13 / 42 1.1 要するに let-function add_two (x : int)

    : int = + x 2 let-function apply_twice (f : int → int) (x : int) : int = f (f x) let-value foo : int = (←C#/Java はここしかできない ) apply_twice add_two 0 自動で最適な型をつけてくれる
  14. 14 / 42 1.2 型推論の特長 • 主に静的型付き言語をターゲットとする • 変数・関数の型がほとんど or

    全く書かれていない コードに • もっとも汎用的な正しい型を当てはめる手法
  15. 15 / 42 1.2 型推論の特長 • 主に静的型付き言語をターゲットとする → 厳格な型チェックによる利点はそのまま •

    変数・関数の型がほとんど or 全く書かれていない コードに → 複雑な型になるコードでも簡単に書ける • もっとも汎用的な正しい型を当てはめる手法 → 失敗しないし信頼できる(ように作る必要がある)
  16. 16 / 42 1.3 型推論の短所 • コンパイル時間が長くなる • 言語の型システムの機能が強すぎると無理 •

    動的型付けと見た目は似てるが,動的型付き言語に あとから追加するのは大変難しい!
  17. 17 / 42 1.3 型推論の短所 • コンパイル時間が長くなる (そのぶん動作は高速だしバグも減らせるけど) • 言語の型システムの機能が強すぎると無理

    (理論的限界.一部だけ書かせるとできたりする) • 動的型付けと見た目は似てるが,動的型付き言語に あとから追加するのは大変難しい! (そもそもベースが静的型付けなので……)
  18. 18 / 42 2 型推論の仕組み • 文脈(=変数・関数名とその型の辞書)と 推論規則(=制約を生成するルール)を用意する

  19. 19 / 42 2 型推論の仕組み • 文脈(=変数・関数名とその型の辞書)と 推論規則(=制約を生成するルール)を用意する • コード中の未知の型変数を文脈に追加し,推論規則

    を用いて型の等値制約(≒型の等式)を生成する
  20. 20 / 42 2 型推論の仕組み • 文脈(=変数・関数名とその型の辞書)と 推論規則(=制約を生成するルール)を用意する • コード中の未知の型変数を文脈に追加し,推論規則

    を用いて型の等値制約(≒型の等式)を生成する • 得られた制約列を単一化して,文脈に追加した 型変数を消す(≒型の連立方程式の解を求める)
  21. 21 / 42 2 型推論の仕組み • 文脈(=変数・関数名とその型の辞書)と 推論規則(=制約を生成するルール)を用意する • コード中の未知の型変数を文脈に追加し,推論規則

    を用いて型の等値制約(≒型の等式)を生成する • 得られた制約列を単一化して,文脈に追加した 型変数を消す(≒型の連立方程式の解を求める) • 必要な結果を文脈に残して次のコードに進む
  22. 22 / 42 2.1 実際にやってみよう • 文脈(=変数・関数名とその型の辞書)と 推論規則(=制約を生成するルール)を用意する – 文脈:

    • + は int 型 2 つを取って int 型を返す ( +: int → int → int ) – 推論規則: • 整数リテラル 0,1,2... は int 型 • 文脈に関数 f: a → b, 値 x: a があるとき 関数適用 f x は b 型 • etc...
  23. 23 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 文脈 + : int → int → int ※ 未知の関数・変数に対して,他と重複 しないように型変数を生成し,文脈に追加する
  24. 24 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 文脈 + : int → int → int, add_two : a → b ※ 未知の関数・変数に対して,他と重複 しないように型変数を生成し,文脈に追加する
  25. 25 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 文脈 + : int → int → int, add_two : a → b, x : c ※ 未知の関数・変数に対して,他と重複 しないように型変数を生成し,文脈に追加する
  26. 26 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 a = c 文脈 + : int → int → int, add_two : a → b, x : c
  27. 27 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 a = c, c = int 文脈 + : int → int → int, add_two : a → b, x : c
  28. 28 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 a = c, c = int, int = int 文脈 + : int → int → int, add_two : a → b, x : c
  29. 29 / 42 2.1 実際にやってみよう • コード中の未知の型変数を文脈に追加し,推論規則 を用いて型の等値制約(≒型の等式)を生成する let-function add_two

    x = + x 2 制約 a = c, c = int, int = int, b = int 文脈 + : int → int → int, add_two : a → b, x : c
  30. 30 / 42 2.1 実際にやってみよう • 得られた制約列を単一化して,文脈に追加した 型変数を消す(≒型の連立方程式の解を求める) let-function add_two

    x = + x 2 制約 a = c, c = int, int = int, b = int 文脈 + : int → int → int, add_two : a → b, x : c
  31. 31 / 42 2.1 実際にやってみよう • 得られた制約列を単一化して,文脈に追加した 型変数を消す(≒型の連立方程式の解を求める) let-function add_two

    x = + x 2 制約 a = int, b = int, c = int 文脈 + : int → int → int, add_two : a → b, x : c
  32. 32 / 42 2.1 実際にやってみよう • 得られた制約列を単一化して,文脈に追加した 型変数を消す(≒型の連立方程式の解を求める) let-function add_two

    x = + x 2 制約 a = int, b = int, c = int 文脈 + : int → int → int, add_two : a → b, x : c
  33. 33 / 42 2.1 実際にやってみよう • 得られた制約列を単一化して,文脈に追加した 型変数を消す(≒型の連立方程式の解を求める) let-function add_two

    x = + x 2 制約 文脈 + : int → int → int, add_two : int → int, x : int
  34. 34 / 42 2.1 実際にやってみよう • 必要な結果を文脈に残して次のコードに進む ( 実装上は,スコープの中に入る前に文脈のバックアップを取り, 結果のうちスコープの外に出るものだけを外側の文脈に追加する

    ) 制約 文脈 + : int → int → int, add_two : int → int, x : int ↓ ローカル変数(引数)
  35. 35 / 42 2.1 実際にやってみよう • 必要な結果を文脈に残して次のコードに進む ( 実装上は,スコープの中に入る前に文脈のバックアップを取り, 結果のうちスコープの外に出るものだけを外側の文脈に追加する

    ) 制約 文脈 + : int → int → int, add_two : int → int
  36. 36 / 42 2.2 Hindley-Milner 型推論 • 例えば は関数 f

    がどんな型であっても使えるはず • このような関数に対して自動的に全称型を付ける(= ジェネリック型にする)ように設計された アルゴリ ズムが Hindley-Milner 型推論 • 中身は先ほど説明した仕組みとほぼ同じで,全称型付 きの関数を文脈に記録する際に工夫をする let-function apply_twice f x = f (f x)
  37. 37 / 42 2.2 Hindley-Milner 型推論 • Hindley-Milner を使うと,先述の例には という型をつけることができる

    • Hindley-Milner の歴史は古く,だいたい 1970 年代ま で遡ることができる • まともな型推論を積んでいる現代の言語は,ほぼ 例外 なく Hindley-Milner かその拡張を使っている let-function apply_twice<T> (f : T → T) (x : T) : T = f (f x)
  38. 38 / 42 2.3 制約単一化型推論の限界 • 型の等値制約を単一化するタイプの型推論アルゴリズ ムは,関数・演算子のオーバーロードに対応できない – 解が複数存在する(が全称型にならない)ので

    「一番汎用的な型」がそもそも存在しない – ユーザが自作型に演算子を定義できるような場合, 解がいくつあるのかすら分からない! – Hindley-Milner にオーバーロード解決を加えると 決定不能になる(=止まらないかもしれない)
  39. 39 / 42 2.3 制約単一化型推論の限界 • ほぼ同じ理由で,オブジェクト指向プログラミングで よく使われる機能への対応が難しい – 同じ名前のメンバやメソッドを持つ型がたくさん存

    在しうるので,それらの使い方から解が定まらない – 継承などの機能で部分型が導入されると,推論結果 の汎用性に部分型関係が絡んできて難しい – 型の解がいくつあるのか分からなくなるような機能 はだいたいヤバイ
  40. 40 / 42 2.4 Further Reading • LT の枠に収めるために端折ったり誤魔化したりした 所が結構ある……

    特に型推論の限界はもっと色々ある • 興味があって詳しく知りたい・実装してみたい方は B. C. Pierce 「型システム入門」を読んでみてね↓ • prog-lang-sys-ja.slack.com の #theory チャンネルに来てもらえれば,込み入った 質問にも随時対応できますので是非〜
  41. 41 / 42 Thank You! • 分かりやすく説明できてたら嬉しいです • 分かっちゃうと怖くない.怖くなくない? •

    型推論で幸せになりたいなら F# っていう言語がおすすめです
  42. 42 / 42 質問タイム(?)