yn2011's blog

技術メモ

Go の型システム周りについてのメモ

Go の型システムについて、今理解していることを書く。もしかしたら間違っているかもしれない。

Go の型システム

  • 型同士に階層がない(サブタイプ・スーパタイプのような関係がない)
    • 型は名前によって区別される。階層がないので、ある型が要求された場合にその型のサブタイプが許容されるようなことはない。
    • ただし、例外としてinterface 型の解決は形状(実装するメソッド)が一致していれば許容
  • 型は named type と unnamed type に分けられる

型の割当可能性

同一の型同士の割当は以下のルールに従う。

同一の型とは、以下を満たす型を指す。

  • named type 同士は名前が一致している(したがって型の実体が一致している)
  • unnamed type と named type は型の実体が一致している
    • unnamed type 同士も同様

以下の表では、同一の型同士であっても割当不可な場合について☓を記載している。

\ named type unnamed type
named type
unnamed type

(例) 2行目3列の場合は☓なので割当できない

// named type to unnamed type
hoge1 := "hoge1"
type a string 
var hoge2 a
hoge2 = "hoge2"
hoge1 = hoge2 // error : cannot use hoge2 (type a) as type string in assignment

ただし、interface 型の場合は例外で形状(実装するメソッド)が一致しているかどうかだけが問題になる

(例)

type c struct{}
func(p c) hoge(){}

func main(){

type hoge interface{hoge()}
var b1 hoge
var b2 interface{hoge()}

b1 = c{} // ok , named type to named type
b2 = c{} // ok , named type to unnamed type
}

複雑な型について

例えば、map[string]interface{}map[string]int は割当可能か?

型に階層関係の存在する言語(型システム)に慣れていると、これは割当可能ではないかと感じるが Go の型には階層関係がないので割当できない。互いに独立した無名型でしかない。

例えばTypeScriptの場合は map 型は以下のように割当できる。

const a = (b : Map<string, any>) => {};
const c = new Map<string, number>([['hoge', 1]])

a(c); // ok

TypeScript は、複雑な型(オブジェクト、クラス、配列、関数の戻り値)等はそのプロパティに関して全て共変である(=サブタイプ or 同じ型を要求すうる)とのこと*1

Go の場合は型に階層自体がないので、変性という概念もないのかもしれない。

Go は duck typing (あるいは structural typing) なのか

そもそも duck typing とは何か、その明確な定義があまりよく分かっていない。duck typing は動的型付言語に対して言う言葉らしいので、duck typing ではなく structural typing という言葉が正しいらしい。

例えば TypeScript は structural typing と言われ、いわゆる named type (名前型) はサポートしていないので以下が通ってしまう。

type a = string
const b = (c :a) => {};
b('hoge'); // ok 

Go は interface 型については structural typing だが、上でも書いたとおり名前型が使えるという点では完全な structural typing な言語とは言えないのでないか? その言語が structural typing である、というとき、それはどういう意味で使われているんだろうか?

Does Golang use structural typing, nominal typing, or both?

上記の回答によると、Go に関しては両方ということになるらしい。まあ確かにそうである。

これも Go が行っている試みの1つなのかもしれない

There are many things in the Go language and libraries that differ from modern practices, simply because we feel it's sometimes worth trying a different approach.

Why does Go not have assertions? より引用

参考

Named and Unnamed Types

*1:プログラミングTypeScript P122