Go言語の型システム再入門
初めに(内容の概略)
Go言語のジェネリクス入門では、型集合という言葉を使わずにジェネリクスの説明をしました。これはジェネリクスを素早く使えるようになりたいであろう読者のニーズに応えるためです。他方で、ジェネリクスを体系的に理解したい読者のために、型集合の概念をきちんと導入する記事も書きたいと考えていました。
そこでそのような記事を「Go言語のジェネリクス入門(理論編)」として書こうとしたのですが、実際にはジェネリクスの有無に関わらない「Goの型システム」の基本から書いた方が良さそうなことに気づきました。例えば私の知る優秀なプログラマーでも、型定義のことを(カジュアルに)エイリアスと呼んでいたりすることがあります。ご存じの読者もいるかと思いますが、Go言語では型定義type MyInt intと型エイリアスtype MyInt = intはそれぞれ異なるものなので、前者をエイリアスと言うのは技術的には誤りです。このように、すでにGoのプログラムが上手に書けるプログラマーにとっても、Goの型システムに再入門するのは、Go言語への理解をよりスッキリさせるために役立つだろうと考えました。
対象読者
Go言語のプログラムを実際に手を動かして書いた経験がある程度ある人
ジェネリクスに関係ない前半部分については、ジェネリクスを使うコードを書いたことがなくても良いです
内容: Go言語の型システム再入門
メソッド・フィールド・演算子はそれぞれ別物である
メソッドと関数は同じだと思って良い
Haskellのように、「関数と演算子が実は同じものである」というようなことはない
型を作る方法
事前宣言された型
型リテラル
型定義
型エイリアスとは異なるものであることに注意
non-interface型同士に部分型関係は成立しない
部分型関係のカジュアルな定義
ある型Aの値vが型Bの値としても扱えるとき、AはBの部分型であるという
「扱える」の例としての代入可能性
interface型とnon-interface型、interface型とinterface型の間には部分型関係が成立しうる
interface型
型集合
実装する(implement)
メソッド集合
Go1.17(ジェネリクスがないころのGo)まではメソッド集合が直接定義されていたが、ジェネリクス以後は型集合を使ってメソッド集合を定義している
ジェネリクス
型制約はinterface型である
型パラメータは型制約を満たす必要がある
「満たす」は「実装する」より少しだけ緩い条件だが、大体の場合は同じだと思って良い
ジェネリクスの理想と現実
理想テーゼ
型制約の型集合に属する全ての型について操作Xができるならば、型パラメータTの値tに対しても操作Xができる
操作Xを「特定のメソッド呼び出し」とすればテーゼはなりたつ
現実: できない操作がある
フィールドアクセス
特定の単項演算子・二項演算子の使用
for range文でループする
型パラメータ型はinterface型ではない
ジェネリクスにおける型推論
型方程式 type equation
型統一 unification
型等価性 equivalence