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

introduction to type sets

Avatar for nobishii nobishii
August 20, 2021

introduction to type sets

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

Avatar for nobishii

nobishii

August 20, 2021
Tweet

More Decks by nobishii

Other Decks in Programming

Transcript

  1. 参考資料(公式) • 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言語仕様書に型パラメータの仕様を反映するためのパッチです。
  2. 参考資料(公式以外) • Goにこれから入る機能について @tenntenn さん ◦ 型推論などこの発表で触れられない事柄含めて広くカバーされています • 入門Go言語仕様/Underlying Type

    @DQNEO さん ◦ underlying typeについてわかりやすく丁寧に書かれています • 発表者 (@shino_nobishii)の記事 ◦ Go の "Type Sets" proposal を読む ▪ type listから型セットに変更されたモチベーションを書いています ◦ Type Sets Proposalを読む(2) ▪ 「実装(implement)する」の判定アルゴリズムと言語仕様の関係を書いています
  3. 例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
  4. 例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] }
  5. 例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が使える
  6. クイズ(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のメソッドセットを包含すること」
  7. メソッドセットとは • 全ての型はメソッドセットを持つ(空集合の場合もある) ◦ メソッドセットの「セット」は数学の教科書に載っている「集合」のこと ◦ メソッドセットはメソッドの集合 • インタフェース型の例 ◦

    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, .... }
  8. 例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] }
  9. 例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を実装する型は全て比較演算子 > を サポートするのでここで > を使える
  10. 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
  11. 型セットによる「実装」の再定義 • 全ての型は型セットを持つ ◦ 型だけではなく、インタフェース要素(後述)も型セットを持つ ◦ 型セットの「セット」も集合のこと 【新しい「実装」の定義】 「ある型Tがあるインタフェース IFを実装する」とは、

    「型TがインタフェースIFの型セットに含まれる」ことを言う 【※従来の「実装」の定義】 「型TがインタフェースIFを実装する」とは、「型 TのメソッドセットがIFの メソッドセットを包含すること」を言う
  12. ジェネリクス以後のインタフェース型 interface { ~int | string fmt.Stringer Print() } メソッド定義

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

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

    練習問題: これを実装する型を定義してみましょう Print() を実装する型か らなる集合 ~intとstringの合併集合 String() string を実装する型 からなる集合 これら3つの共通部分: - String() stringとPrint()を両 方実装し、かつ、 - string型であるか、 underlying typeがintであ る の両方を満たす型からなる集合
  15. まとめ(2) 型セットのルールと「実装(implement)」 • インタフェース型以外の型の型セットは、その型のみからなる集合 • 空インタフェース interface{}の型セットは、全ての型からなる集合 • 空インタフェース以外のインタフェースの型セットは、そのインタフェース要素の型 セットの共通部分

    • インタフェース要素の型セットは次のように決まる ◦ メソッド定義の型セットは、そのメソッドをメソッドセットに含む全ての型からなる集合 ◦ ~Tの型セットは、underlying typeがTである全ての型からなる集合 ◦ T1 | T2 | … Tn というunionの型セットは、T1...Tnの型セットの合併集合 「型TがインタフェースIFを実装する」とは、 「型TがインタフェースIFの型セットに含まれる」ことを言う
  16. 型セットのコードをローカル環境で動かす go install golang.org/dl/gotip@latest gotip download dev.typeparams gotip run -gcflags="-G=3"

    main.go # go1.17 run -gcflags="-G=3" main.go でも型セット部分なしの型 パラメータなら動きます 参考: Type Sets Proposal 勉強会用メモ by @syumai さん
  17. 練習問題解答例 // 解答例 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()のメソッド宣言がはいる(省略)