入門Go言語仕様輪読会Untyped Constants@DQNEO2021-04-15
View Slide
自己紹介● @DQNEO (ドキュネオ)● Goコンパイラ babygo の作者○ https://github.com/DQNEO/babygo● 公式Goコンパイラ、言語仕様書コントリビュート歴有り
問題: 次の値の型は何でしょうか?123"hello"true
答え: なし123"hello"true← 型なし← 型なし← 型なし
問題: 次の2文は何が違うでしょうか?const x = 123var x = 123注: const / var 違いの他に
答え: 型が違うconst x = 123var x = 123var x int = 123両辺とも型なし両辺とも int=
型なし?
Go言語の定数は● 型あり● 型なしのどちらか
これらは全部型なし定数● 123● 1.1● "hello"● ‘a’● true● 1.0+3.0i
型なし定数を作ることも可能const THREE = 3const HELLO = "hello"左辺で型を省き、右辺で型なし定数を使う
型なし定数のおもしろ性質(1)_人人人人人人_> 型がない < ̄Y^Y^Y^Y^Y^Y^ ̄
型がないのでユーザ定義型に代入可能type MyBool boolvar b MyBool = trueもし true が bool 型だったら、代入できないはず(代入可能性の解説はこちら )https://play.golang.org/p/63VHrn7XQVY
const HELLO = "hello"type MyString stringvar s MyString = HELLOもし HELLO が string型だったら、代入できない型がないのでユーザ定義型に代入可能(代入可能性の解説はこちら )
型がないのでいろんな型に代入可能var y float32 = 97var x int64 = 'a'var z byte = 97.0(※ ただし “representable” な場合に限る)どれも 右辺は 「左辺の型の97」扱い
型なし定数のおもしろ性質(2)「型を隠し持っている」
型なし定数にも種類があるリテラル 種類1 integer constant1.0 floating-point constant'a' rune constant"hello" string constanttrue boolean constant
種類に応じて default type があるリテラル 種類 default type1 integer constant int1.0 floating-point constant float64'a' rune constant rune(=int32)"hello" string constant stringtrue boolean constant bool
var x = 1.0型が必要です。あなたの型を教えてくださいfloat64 です聞かれたときだけ答えるdefault type とは、「型を要求されたとき」に使われる型
型を要求される文脈の例● var x = 123● y := 1.0● var ifc interface{} = 1.0● fmt.Printf("%v", 1.0)
● var x uint8 = 1.0私は uint8型です。私に従ってくださいわかりましたdefault typeは、必ずしも使われるわけではないuint8として振る舞うdefault typeは使われない
型なし定数のおもしろ性質(3)「すごい数もOK」
超巨大数を定義できるconst HUGE = 92233720368547758070↑ int64の最大値より大きい
定義できるが表示できないconst HUGE = 92233720368547758070fmt.Println(HUGE)_人人人人人人_> コンパイルエラー < ̄Y^Y^Y^Y^Y^Y^ ̄constant 92233720368547758070 overflows int64
int型変数に代入できないconst HUGE = 92233720368547758070var x = HUGEconstant 92233720368547758070 overflows int64_人人人人人人_> コンパイルエラー < ̄Y^Y^Y^Y^Y^Y^ ̄
Go言語氏「default type があるとは言ったが、default type に収まるとは言ってない」
float型変数には代入できるconst HUGE = 92233720368547758070var x float64 = HUGEfmt.Printf("%v\n", x) => 9.223372036854776e+19精度は落ちる (当然)
巨大数同士の定数計算ができるconst HUGE = 92233720368547758070const X_HUGE = 922337203685477580700var x uint8 = X_HUGE / HUGE計算結果がuint8 に収まるのでOK=> 10
超細かい小数を定義できるconst ROOT2 =1.41421356237309504880168872420969807856967187537694807317667974float64 の精度を超えている
二乗すると 2 になるconst ROOT2 =1.41421356237309504880168872420969807856967187537694807317667974fmt.Println(ROOT2 * ROOT2) // => 2_人人人人人人_> 精度高すぎ < ̄Y^Y^Y^Y^Y^Y^ ̄
変数を経由すると精度が落ちるconst ROOT2 =1.41421356237309504880168872420969807856967187537694807317667974f := ROOT2 // ここで float64に詰め替えられるfmt.Println(f * f) // => 2.0000000000000004https://play.golang.org/p/JMtw6IZjDtP
Goの言語思想“they are just regular numbers”型なし定数の数値は「ただの数」であるconst HUGE = 92233720368547758070const ROOT2 =1.41421356237309504880168872420969807856967187537694807317667974https://blog.golang.org/constants
Goの言語思想“they live in a kind of ideal space of values”型なし定数は、型の制約のない自由な理想空間で生きている
Goの言語思想11.0001e3-99.0*10-9'\x01'‘a’ (= ‘0x97 ’ 97)'\u0001''b' - 'a'1.0+3i-3.0iこれらはどれも 型なし定数 1 と同等に扱える
おまけこの辺は例外的な仕様● string(97) // => "a"● string(97.0) // コンパイルエラー
ご清聴ありがとうございました