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

introduction to type sets

nobishii
August 20, 2021

introduction to type sets

Go言語のジェネリクス(型パラメタ)導入後の言語仕様には、型セット(type sets)というものが導入されます。このスライドではその内容と必要性を説明します。

nobishii

August 20, 2021
Tweet

More Decks by nobishii

Other Decks in Programming

Transcript

  1. 初めての型セット Go1.17 Release Party 2021/08/20 @shino_nobishii

  2. 自己紹介 • Nobishii (@shino_nobishii) • 1年半くらい仕事でGo言語を使っています • Go言語仕様書ファン ◦ #gospecreading

  3. この発表について • ジェネリクス・型パラメータに関係する発表です • 型パラメータの実用よりも、次の「考え方」の理解を目指します ◦ ある型があるインタフェースを実装 (implement)するとはどういうことか ◦ メソッドセットとは何か

    ◦ 型セットとは何か・どう決まるか ◦ 型セットとメソッドセットはどういう関係にあるか • 実用例は次の@syumai さんの発表をお楽しみに!
  4. 参考資料(公式) • Type Parameters Proposal ◦ 型パラメータのまとまったドキュメントです。 ◦ 型セットの部分がtype listになっているところだけは古い内容なので注意してください。

    ◦ 型推論アルゴリズムや豊富な具体例など現在ここにしか書かれていない内容は多いです。 • spec: generics: use type sets to remove type keyword in constraints · Issue #45346 · golang/go ◦ type listの代わりに型セットを使うプロポーザル。ステータスは Acceptedです。 • The Go Programming Language Specification ◦ 現在のGo言語仕様書です。 • Go言語仕様書のレビュー中のCL ◦ Go言語仕様書に型パラメータの仕様を反映するためのパッチです。
  5. 参考資料(公式以外) • Goにこれから入る機能について @tenntenn さん ◦ 型推論などこの発表で触れられない事柄含めて広くカバーされています • 入門Go言語仕様/Underlying Type

    @DQNEO さん ◦ underlying typeについてわかりやすく丁寧に書かれています • 発表者 (@shino_nobishii)の記事 ◦ Go の "Type Sets" proposal を読む ▪ type listから型セットに変更されたモチベーションを書いています ◦ Type Sets Proposalを読む(2) ▪ 「実装(implement)する」の判定アルゴリズムと言語仕様の関係を書いています
  6. Goのジェネリクスの要点 ※リリースはGo1.18以後。細部はまだ変わるかもしれません • 「関数」と「型」は、「型パラメータ」を持つことができる • 型パラメータが満たすべき型制約は、インタフェースによって表現する • ある型TがインタフェースIFで表される型制約を満たすのは、型TがインタフェースIF を実装(implement)するときである 参考:

    Type Parameters Proposal
  7. 例1: Map(型パラメタを持つ関数) func Map[T1, T2 any](s []T1, f func(T1) T2)

    []T2 { r := make([]T2, len(s)) for i, v := range s { r[i] = f(v) } return r } func main() { s := Map([]int{1,2,3}, func(x int) string { return fmt.Sprintf("String: %d", x) }) fmt.Println(s) // [String: 1 String: 2 String: 3] } // 以上 Type Parameters Proposalから引用 https://go2goplay.golang.org/p/WWXh6ThtGFY
  8. 例2: sortとSortable(型パラメータを持つ型) type Item int func(i Item) CompareTo(j Item) int

    { return int(j-i) } // 降順ソート func main() { xs := &Sortable[Item]{1,3,2,4,5,8,7} sort.Sort(xs) fmt.Println(xs) // &[8 7 5 4 3 2 1] }
  9. 例2: sort type Comparable[T any] interface { CompareTo(T) int }

    type Sortable[T Comparable[T]] []T // この型にsort.Interfaceを実装させる func (s *Sortable[T]) Less(i, j int) bool { return (*s)[i].CompareTo((*s)[j]) < 0 } // LenとSwapメソッドも必要ですが省略します https://go2goplay.golang.org/p/1SWrvbqCuv8 型パラメタ付きの interface型 interface型を型制約に用いる。 Item型はComparable[Item]を満たす 型制約のおかげで CompareToが使える
  10. 型制約はインターフェースで表される • Goのジェネリクスは、型制約をインターフェースで表す • 「型制約を満たす」は「インタフェースを実装する」と同義 • Comparable型制約を満たすためには、Comparableインタフェースを実装すれば よい • 「実装する」の意味が重要になる

    type Comparable[T any] interface { CompareTo(T) int }
  11. クイズ(Go1.16や1.17でお考えください) 「型TがインタフェースIを実装する」とはどういう意味でしょうか?

  12. クイズ(Go1.16でお考えください) 「型TがインタフェースIを実装する」とはどういう意味でしょうか? https://golang.org/ref/spec#Interface_types より引用: A variable of interface type can

    store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. 【解答例】 「型TがインタフェースIFを実装する」とは、 「TのメソッドセットがIFのメソッドセットを包含すること」
  13. メソッドセットとは • 全ての型はメソッドセットを持つ(空集合の場合もある) ◦ メソッドセットの「セット」は数学の教科書に載っている「集合」のこと ◦ メソッドセットはメソッドの集合 • インタフェース型の例 ◦

    io.Readerのメソッドセットは Readメソッドのみからなる集合 { Read } ◦ io.ReadCloserのメソッドセットは { Read, Close } ◦ io.ReadWriteCloserのメソッドセットは{ Read, Write, Close } • 非インタフェース型の例 ◦ *os.Fileのメソッドセットは { Read, Write, Close, Sync, … }  ◦ *bytes.Bufferのメソッドセットは { Read, Write, Bytes, … } ◦ *gzip.Readerのメソッドセットは { Read, Close, .... }
  14. 「型TがインタフェースIを実装する」とは、 「TのメソッドセットがIのメソッドセットを包含すること」

  15. 例3: sortとSortable再び type Item int // 今度はCompareToを実装しない func main() {

    xs := &Sortable[Item]{1,3,2,4,5,8,7} sort.Sort(xs) fmt.Println(xs) // &[8 7 5 4 3 2 1] }
  16. 例3 type Item int // 再掲 type Number interface {

    ~int | ~float64 } type Sortable[T Number] []T func (s *Sortable[T]) Less(i, j int) bool { return (*s)[i] > (*s)[j] } // LenとSwapは省略しました https://go2goplay.golang.org/p/Q6dMbKH-x-u 新しい文法。underlying type(※後述)が intまたはfloat64である型はNumberを実装する。 Numberを実装する型は全て比較演算子 > を サポートするのでここで > を使える
  17. underlying type • 全ての型にはunderlying typeがある • type A Bのように型定義されたAのunderlying typeは次のように決まる

    • AからBに遡り、Bの型定義があればさらに遡ることを繰り返す • 最終的には次のような型に辿り着き、それ以上遡れなくなる ◦ int ◦ []string ◦ *float64 ◦ struct { ID int; Name string } • 行き着いた終点にある型がAのunderlying type • 参考資料 ◦ Go言語仕様書 ◦ https://speakerdeck.com/dqneo/go-language-underlying-type
  18. ちょっと待って。「実装する」ってなんだっけ? • 「型制約を満たす」は「インタフェースを実装する」と同義 • 「型TがインタフェースIを実装する」とは、「TのメソッドセットがIのメソッド セットを包含すること」だったはず(Go1.16) • Numberのメソッドセットって何? • 「実装(implement)」概念のアップデートが必要

    type Number interface { ~int | ~float64 }
  19. 型セットによる「実装」の再定義 • 全ての型は型セットを持つ ◦ 型だけではなく、インタフェース要素(後述)も型セットを持つ ◦ 型セットの「セット」も集合のこと 【新しい「実装」の定義】 「ある型Tがあるインタフェース IFを実装する」とは、

    「型TがインタフェースIFの型セットに含まれる」ことを言う 【※従来の「実装」の定義】 「型TがインタフェースIFを実装する」とは、「型 TのメソッドセットがIFの メソッドセットを包含すること」を言う
  20. 型セットのルール(一部) • インタフェースではない型の型セットは、その型のみからなる集合 ◦ intの型集合は{int} • メソッド定義のみを持つインタフェース型の型セットは、それらのメソッド全てを実装する型全体からなる集合 • 実装の「新しい定義」と「従来の定義」で結論が一致することがわかる メソッドセット

    型セット
  21. メソッドセットと型セットの関係 メソッドセット 型セット

  22. ジェネリクス以後のインタフェース型 interface { ~int | string fmt.Stringer Print() } メソッド定義

    (MethodSpec) 型表式(TypeExpr) 項(TypeTerm)を複数持つ。 このようなものをunionsと言う 型表式(TypeExpr) 項(TypeTerm)を1つだけ持つ インタフェース型は0個以上のイン タフェース要素(InterfaceElem)か らなる。 TypeExprもMethodSpecもインタ フェース要素の一種。
  23. 型セットのルール(一般の場合) • インタフェース型以外の型の型セットは、その型のみからなる集合 • 空インタフェース interface{}の型セットは、全ての型からなる集合 • 空インタフェース以外のインタフェースの型セットは、そのインタフェース要素の型 セットの共通部分 •

    インタフェース要素の型セットは次のように決まる ◦ メソッド定義の型セットは、そのメソッドをメソッドセットに含む全ての型からなる集合 ◦ ~Tの型セットは、underlying typeがTである全ての型からなる集合 ◦ T1 | T2 | … Tn というunionの型セットは、T1...Tnの型セットの合併集合
  24. ルールを当てはめてみる interface { ~int | string fmt.Stringer Print() } //

    練習問題: これを実装する型を定義してみましょう Print() を実装する型か らなる集合 ~intとstringの合併集合 String() string を実装する型 からなる集合 これら3つの共通部分: - String() stringとPrint()を両 方実装し、かつ、 - string型であるか、 underlying typeがintであ る の両方を満たす型からなる集合
  25. まとめ(1) なぜ型セットの概念が必要か • 型パラメータProposalは型制約をインタフェースで表す • 「型制約を満たす」は「インタフェースを実装する」と同じ意味としたい • <や==による比較可能性などは従来のインタフェースで表せない • そこで「インタフェース型」で表せるものを拡張することにした

    • それに合わせて「実装(implement)する」の概念も拡張する必要が生じた • 新しい「実装する」を表現することばとして「型セット」の概念が生まれた
  26. まとめ(2) 型セットのルールと「実装(implement)」 • インタフェース型以外の型の型セットは、その型のみからなる集合 • 空インタフェース interface{}の型セットは、全ての型からなる集合 • 空インタフェース以外のインタフェースの型セットは、そのインタフェース要素の型 セットの共通部分

    • インタフェース要素の型セットは次のように決まる ◦ メソッド定義の型セットは、そのメソッドをメソッドセットに含む全ての型からなる集合 ◦ ~Tの型セットは、underlying typeがTである全ての型からなる集合 ◦ T1 | T2 | … Tn というunionの型セットは、T1...Tnの型セットの合併集合 「型TがインタフェースIFを実装する」とは、 「型TがインタフェースIFの型セットに含まれる」ことを言う
  27. 補足資料集 • 発表は以上です • 以下は補足資料です

  28. メソッドセットの定義も更新される • インタフェース型のメソッドセットは、その型セットに属する型のメソッドセットの共通 部分である • 型パラメータのメソッドセットはその型制約のメソッドセットに等しい • それ以外の型のメソッドセットは従来通り

  29. ※数式の方がわかりやすい人向け

  30. 型セットのコードをローカル環境で動かす go install golang.org/dl/[email protected] gotip download dev.typeparams gotip run -gcflags="-G=3"

    main.go # go1.17 run -gcflags="-G=3" main.go でも型セット部分なしの型 パラメータなら動きます 参考: Type Sets Proposal 勉強会用メモ by @syumai さん
  31. 練習問題解答例 // 解答例 type MyInt int func(x MyInt) String() string

    { return fmt.Sprintf(“%d”, x) } func(x MyInt) Print() { fmt.Println(x.String()) } // なお、次のMyStringは不正解です。 // MyStringはstringの型セットの要素ではないからです。 type MyString string // ここにString() stringとPrint()のメソッド宣言がはいる(省略)