📖

Goの言語仕様書精読のススメ & 英語彙集

2021/03/01に公開
13

この記事について

Go言語公式から提供されているThe Go Programming Language Specificationという文章があります。

実際のThe Go Programming Language Specificationのページ画面

この文章、個人的にはじっくり読んでみると結構得るものが大きいな、と感じるものです。本記事では

  • The Go Programming Language Specificationって何が書いてあるの?
  • 読んだら何がわかるの?
  • 読むときにはどういうところに注目したらいいの?
  • 英語難しいから単語教えて!

という疑問に答えながら、The Go Programming Language Specification精読の布教を行います。

The Go Programming Language Specification とは?

The Go Programming Language Specificationとは、Goの言語仕様が書いてある文書です。

言語仕様なので、「どんな型があるのか」「どんな演算子があるのか」というおなじみの内容だけでなく、「そもそもソースコード中に使える文字は何か」や「ある型の変数に値を代入できる条件は何か」といった地固め的な内容も含まれます。

以下の表で、GoSpecの章立てとそこで定義されている内容についてまとめました。

定義されている内容
Introduction ---
Notation EBNFによる表記方法
Source code representation Unicodeのどの文字を使用するか
Lexical elements 字句解析におけるtokenをどう定めるか
Constants 「定数」そのものとその型
Variables 変数宣言と型
Types Goで使用できる型の仕様
Properties of types and values 型と変数に絡んだ概念(型同一性など)
Blocks コードブロック
Declarations and scope 〇〇宣言と表現されるもの全てとそのスコープ範囲
Expressions Goで使える式
Statements Goで使える文
Built-in functions ビルトイン関数
Packages パッケージ宣言やインポート宣言といったパッケージ周りの記述方式
Program initialization and execution 変数・パッケージの初期化の挙動とプログラム実行開始位置
Errors エラーインターフェース
Run-time panics ランタイムパニック
System considerations その他補足事項

読んで得られたもの

  • 「今更言語仕様書なんて読まなくても、私Go言語そこそこ書けるからだいじょーぶ!」
  • 「こんな長くて難しい文章読んで何が嬉しいの???」
  • 「読んだらどういうスキルが身につくの?」

と思っているそこのあなたに、筆者が感じた「GoSpecを読んで得られそうなもの」を挙げていきたいと思います。

重箱の底にあるような細かい仕様がわかる

早速ですが問題です。
以下のコードを実行したらなんと出力されるでしょう。

var x int8 = -128
fmt.Println(x/-1)

The Go Playgroundでの実行はこちら

驚くことに、答えは-128です。
「負の数を負で割ったのに答えが負なの??Why???」と思うでしょう。私も最初はそう思いました。
この挙動については、GoSpecのArithmetic operatorsの章に、以下のように記述されています。

if the dividend x is the most negative value for the int type of x, the quotient q = x / -1 is equal to x (and r = 0) due to two's-complement integer overflow.

(和訳) 割られる数xがその型で表現可能な最小の負値である場合、2の補数表現でのオーバーフロー防止のために、x/-1の商qの値はxと等しくなります。(そして余りr0になります。)

今回の例の場合、2の補数表現を使って8bit(=int8)で表現できる数の範囲は-128~127です。そのため、数学でやるように商を128としてしまうと、解がint8に収まらないのです。そのため、このような特殊な挙動を定義しているのです。

こういうところからGo Quiz[1]のネタができるんだなあという感想を持ちました。

デバッグ力がつく

例その1

「なんでこんな記述あるんだ???」というほど、当たり前のように思える文章に出会うことがあります。
例えば、以下の記述を見てみましょう。

The return parameters of the function are passed by value back to the caller when the function returns.

(和訳) 関数の戻り値は、return文が呼ばれ復帰するときに呼び出し元に値渡しされる。

v := f()とかいう記述のときに、関数f()の戻り値の値がreturn文が呼ばれたときの値になり、それがvに入るのは当たり前のように感じます。
しかし、以下のような関数に副作用があるケースを考えてみましょう。

var s = 1

func f() int {
	s++
	return s
}


func main() {
	v := f()
	v++
	fmt.Println(s)
	fmt.Println(v)
}

The Go Playgroundでの実行はこちら

このとき、出力される値は23です。
v := f()が呼び出されたときの戻り値sの値は2です。そのためvには2が格納されます。その後vがインクリメントされていますが、v := f()では値渡しをされているので、このインクリメントによってsが影響を受けることはありません。そのためこのような結果になります。

といったように、書いてある内容を検証する際に、「この場合はどうだ?」「あの場合はどうだ?」と考えるうちにぶっとんだ例を思いつけるように訓練されていきます。
おそらくこれはデバックするときに使う思考回路と似たものがあるのではないでしょうか。

例その2

例えば、関数の引数として可変数引数を渡せるときに、その引数として何も指定しなかった場合、どうなるでしょうか。

func main() {
	// こう呼び出したときに引数pは何になるのか?
	// nil? それとも空スライス?
	f()
}

func f(p ...string) {
	fmt.Println(p == nil)
}

The Go Playgroundでの実行はこちら

このときの挙動はExpressions-Passing arguments to ... parametersの部分に明確に記されています。

If f is invoked with no actual arguments for p, the value passed to p is nil.

(和訳)もし関数fが、可変数引数pに何も渡さない状態で呼び出された場合、pに渡される値はnilとなります。

このように「可変数引数に何も渡さないとnilになる」などの、特殊なパターンでのデフォルト値・ゼロ値を頭に入れておくことで、例外分岐やデバックがスムーズになりそうな感じがします。

コンパイラが何を見ているのかがなんとなくわかる

GoSpecは「人間がGoコードを解釈する」ために書かれたのではなく、「コンパイラがGoコードを解釈する」ために書かれた側面が強いです。

例えば随所に散りばめられているEBNF記述です。以下はIf statementsの節に記載された、if文をどのように記述するのかをEBNFというメタ文法記法で表現したものです。

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .

これを見て「Goでif文ってこんな感じに書けばいいんだ!」といってコードをゴリゴリ書けちゃう人は本当にごく少数じゃないでしょうか。
このIfStmtの記述は、「コンパイラがこのEBNFに基づいて構文解析をしています」ということを主張する内容です。つまり「まず"if"という文字列があって、次に条件を示すSimpleStmtが規定の書き方であって……」ということをコンパイラはやっているのです。なんとなく雰囲気がわかるでしょうか。

また、他にもGoSpec本文中には「字句解析で使うトークンの種類」だったり「この演算はコンパイル時にこんな感じに最適化されることがあります」といったコンパイラのための記述がたくさんあります。
そのため、読み進めていくとコンパイラの気持ちがなんとなくわかるようになっていく……かもしれません。

あまり期待しない方がいいもの

そして、「これを期待して読むのはちょっとやめた方がいいんじゃないかな?」ということも一応述べておきます。

Go初学者の方が文法をこれをみて学ぼうとすること

繰り返しますが、これは「コンパイラがGoコードを解釈する」ために書かれたものであって、「人間がGoコードを解釈する」ためのものではありません。
GoSpecはインターネット上でタダで見られる文章ではあるのですが、Goの基本的な書き方を普通に知りたいというかたは、お金を浮かせようとはせずに普通に市販の本を買った方がいいでしょう。

読むときに気を付けること

ここからは、ただ読むだけではなく、読んできちんと得るものを得るためにはどういう意識を持った方がいいか?ということについて、筆者が感じたことを書いていきます。

その記述があることで何が嬉しいのか?ということを意識する

GoSpecは言語仕様書であり、全ての記述には意味があるはずです。

例えばTypesの章で導入される「全ての型にはunderlying typeがある」という概念は、一見すると「なんでこんなものを導入するんだ??」と思うかもしれません。

Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration.

(和訳) 全ての型Tにはそれぞれunderlying typeが存在します。Tがブール型・数値型・文字列型・型リテラルの場合、underlying typeはTそのものになります。それ以外の場合、underlying typeはTの型定義に使われている型のunderlying typeと同じになります。

しかし、このunderlying typeはassignability(代入可能性)を定義するために不可欠なものです(後述)。

このように「この記述はどこで役に立つの?」という意識を持つことで、全体像の理解につながります。

常に例を考える

某書籍に「例示は理解の試金石」という言葉があります。
https://twitter.com/hyuki/status/985985934452649984
これは本当にそうで、読んだ内容を元に「こういうコードはこの記述を元に確かにこういう挙動をする」という例が作れるかどうかで理解の深さが段違いだという実感があります。

具体例作りの具体例

例えば、「ある値xが型Tの変数に代入可能(assignable)である」ことの条件の一部として、以下のような定義があります。

  1. xの型Vと型Tが同一(identical)のunderlying typeをもち、かつ型Vと型Tの少なくともどちらか一つがdefined typeでないこと
  2. xがuntypedな定数であり、かつ型Tで表現可能(representable)であること

例えば、以下のように定義されたMyIntSlice型に[]intを代入するのはOKです。

type MyIntSlice []int

func main() {
	var src = []int{0, 1, 2}
	var dst = MyIntSlice{3, 4, 5}
	
	dst = src
	
	fmt.Printf("%T, %v\n", dst, dst)
}
/// main.MyIntSlice, [0 1 2]

The Go Playgroundでの実行はこちら

なぜこのような挙動になるのかというと、MyIntSlice[]intのunderlying typeはどちらも[]intで同じであり、かつ[]intはdefined typeでないからです。
これはassignableの定義1にぴったり当てはまります。

ですが、以下のMyInt型変数にint型を代入するのはNGです。
MyIntintのunderlying typeはどちらもintで一致しますが、どちらもdefined typeなので代入できないのです。

type MyInt int

func main() {
	var src int = 1
	var dst MyInt = 2
	
	dst = src
	
	fmt.Printf("%T, %v\n", dst, dst)
}
// ./prog.go:13:6: cannot use src (type int) as type MyInt in assignment

The Go Playgroundでの実行はこちら

では、以下のように一部をコメントアウトしたらどうなるでしょうか?

type MyInt int

func main() {
-	var src int = 1
	var dst MyInt = 2
	
-	dst = src
	
	fmt.Printf("%T, %v\n", dst, dst)
}

The Go Playgroundでの実行はこちら

やっていることとしては、MyInt型に2を代入していることです。さっきもコンパイルエラーになったんだから今回もダメなんじゃないの?というかたは残念不正解です。

正解はmain.MyInt, 2と出力され、きちんと期待通りの動作が得られます。これは変数dstに代入している2は、扱いとしてはint型ではなく「untypedな定数」となるからです。
assignableの定義2から、2MyIntに代入可能というわけです。

具体例でわかったこと

このように、代入可能性の例を作る過程において

  • ある型をみてそのunderlying typeがわかるかどうか
  • defined type, untyped, 表現可能性といった概念の理解

が問われるわけで、その結果「『ある変数にある値を代入できるか?』というのは『当たり前』で済ませる概念ではない」という感覚が養えるのです。

言語仕様書内で出てくる固有概念を体にしみつかせる

では、前述したように具体例をスラスラ出せるようにするためにはどうしたらいいでしょう。
それには、例えば「untypedな定数とは何?具体的にはどう作るの?」「defined typeってなんだっけ?」とかそういう固有概念がパッと出てくるようにしないとダメなんです。

固有概念の定義についてきっちり体に染み込ませておけば、「これとそれは違う」という小さな差異にも気付ける・頭を巡らせられるようになります。
そうすると「じゃあここを変えたらどういう挙動になる?」と検証するような具体例が作れるようになります。

例えば、GoSpec内では定数変数は別章に分けられています。つまり2つは「違うもの」なのです。
この意識があると、以下のように「シフト回数に負定数使っちゃダメって書いてあるけど、じゃあ変数使っててそれが負だった場合どうなる?」という発想が出てきやすいです。

func main() {
	const negConst int = -1
	var negVar int = -1
	
	_ = 1 << negVar  // panic: runtime error: negative shift amount
	_ = 1 << negConst  // invalid negative shift count: -1
}

The Go Playgroundでの実行はこちら

読み進めるにあたって重要な概念

ここからは、筆者が実際にGoSpecを読み進めるにあたって「この知識・英単語を知ってるとスムーズかも?」と思ったものを書き留めたものです。よく知っている内容があったら適宜飛ばして読んでください。

コンパイラまわりの知識

コンパイラは、原始プログラム(=ソースコード)から目的プログラムを生成するツールです。
つまり、人間が書いたソースコードを、機械語翻訳されたオブジェクトコードを生成するのがコンパイラが行う処理です。
この処理過程は以下の5段階に分けることができます。

  1. 字句解析
    ソースコードを字句単位に分割します。
    例えば、a := 1 + 2というソースコードを字句に分割したら、a := 1 + 2となります。
  2. 構文解析
    分割された字句が、文法的に正しく並んでいるかをチェックします。一般的に構文木が作れるかどうかで確認されます。
  3. 意味解析
    構文が確定した後に、その字句が変数なのか、型はあっているのかといった意味内容面をチェックします。
  4. 最適化
    効率よくプログラムを実行するための最適化を行います。
    例えば、乗算x*2を左シフトx<<1に直して効率化するなど。
  5. 機械語生成

https://www.ebiebievidence.com/posts/2020/12/golang-compiler/

これを前提にしたらわかる!という英単語がいくつかあります。

token

字句解析で分割生成される「字句」「単語」「トークン」のこと。lexical tokenともいう。

GoSpecによると、Goではtokenは4種類に分けられる、とされています。

  • keywords
  • operators and punctuation(演算子と句読点)
  • literals
  • identifiers

x := 1 + 23 ;と分けられて、それぞれ identifier, operator, literal, operator, literal, punctuation となる。

keyword

いわゆる予約語のこと。

そのままキーワードと訳すなかれ。プログラミングの世界においては「予約語」をこういうことが多いです。少なくともC言語JavaScriptはそう言っているのが確認できます。

literal

リテラルは、ざっくりいうとソースコード内に直接書かれた値のことです。
(例)1(int), 2.71828(float), 1.e+0i(complex), 'a'(rune), "abc"(string)

このように例であげたリテラル以外にも、Goで「リテラル」と呼ばれるものは存在します。
例えば型リテラルです。[]intとか*intとかはそれぞれintスライス型、intポインタ型を表す型リテラルです。

なんでこれがint型リテラルやstringリテラルと同じ型"リテラル"という名前なの?という説明としてはtenntennさんの過去ツイートがわかりやすいです。

リテラルなので数値リテラル(100)とか文字列リテラル("hoge")とかみたいに識別子(名前)が付いていない、それそのものを表すもの。intやio.Writerは名前がついてるけど、[]intは型自体に名前がついてる訳ではない。

Goでリテラルと呼ばれるものを挙げると以下の通り。

  • 整数リテラル
  • 小数点リテラル
  • 複素数リテラル
  • runeリテラル
  • 文字列リテラル
  • 型リテラル
  • compositeリテラル
  • 構造体リテラル
  • 配列リテラル
  • スライスリテラル
  • マップリテラル
  • 関数リテラル

いっぱいありすぎでは??

identifier

変数名や型名を表す識別子・名前のこと。
ざっくりいうなら、トークン(字句)の中で、リテラルでも演算子でも予約語でもないものというのが手っ取り早い(要検証)。

このidentifierという単語は、GoSpec本文中に今後よく出てくることになります。本当に。IT用語じゃなくて一般名詞だと思ってしまうと読解が難しくなってしまうので注意しましょう。

EBNFの概念

EBNFはプログラム言語の文法を記述するためのメタ言語の一つです。記述の終わりは.をつけることでで表されます。

GoSpecのNotation章には、EBNFの記法をEBNF自体で表して定義しています。

Production  = production_name "=" [ Expression ] "." .
Expression  = Alternative { "|" Alternative } .
Alternative = Term { Term } .
Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
Group       = "(" Expression ")" .
Option      = "[" Expression "]" .
Repetition  = "{" Expression "}" .

これは要するに以下の内容をいっているだけにすぎません。

  • 何か新しいものに名前をつけて定義したいときは、production_name = (定義内容)と書けばOK
  • |で区切ることで、orを表現できる
  • "要素"を一個以上並べてフレーズ(=Alternative[2])を作ることができる
  • "要素"の定義は「EBNFで定義された何かの名前 or token or (){}[]で囲ったもの」
  • ()で囲うことで要素をグループ化できる
  • []で囲うと、中身を0回または1回使うことを示す
  • {}で囲うと、中身を0回以上繰り返すことを示す

具体例

Alternativeの一例として、Struct typesの節で登場するFieldDeclを紹介します。

FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .

このFieldDeclは、(IdentifierList Type | EmbeddedField)というグループ要素と[Tag]という要素を2個羅列しているAlternativeだと読めます。

Go特有の概念

underlying typeやassignableといった、Go特有の固有概念を辞書順に並べました。
「この概念があることでどこで嬉しいのか?」「何に使われているのか?」というところを意識した方がいいのがこのへんの単語です。

addressable

あるオペランドがaddressableであるということは、

  • 変数
  • ポインタ変数をデリファレンスしたもの
  • スライスaのインデックスを指定してa[i]としたもの
  • addressableな構造体変数のフィールドセレクタstc.fld
  • addressableな配列aのインデックスを指定してa[i]としたもの

であると定義されている。
要するに、addressableであるということは「そのポインタが取得できる」というオペランドの性質のことをいいます。

オペランドにある演算や操作を行うための前提条件として、addressableであるということが随所で出てきます。
(例) slice expression, インクリメント/デクリメント

assign to a variable

「変数に値を代入する」という言い回し。

(例文) It is the most recent value assigned to the variable.

assignable(assignability)

ある値xが型Tにassignableであるということは、「型Tの変数に代入することができる」という値xの性質のことです。
定義内容についてはGoSpec本文を参照のこと。

yyh-glさん(@yyh_gl)のこちらのスライドにもわかりやすい説明があります。
https://go-talks.appspot.com/github.com/yyh-gl/slide-decks/210318_gospecreading_assignability.slide#1

base type

何型に対してのポインタなのかというのを、base typeと表現されます。type identityの定義にからむ概念となります。
例えば、int型へのポインタ*int型のbase typeはint型です。

また、メソッドについているレシーバーの型名についても「レシーバーのbase type」と表現されます。
(例) Readメソッドfunc (p T) Read(p []byte) (n int, err error) のbase typeは型T

bind

識別子(identifier)に変数とか定数とかパッケージ名とか、何かを紐付けすることをbindするといいます。

(例文) A constant declaration binds a list of identifiers (the names of the constants) to the values of a list of constant expressions.

また、メソッドを型に対応させることもbindすると表現されます。

(例文) The method is said to be bound to its receiver base type and the method name is visible only within selectors for type T or *T.

constants

定数。

「リテラルと何が違うの?」と思った方々、奇遇ですね、私と同じです。

A constant value is represented by a rune, integer, floating-point, imaginary, or string literal, an identifier denoting a constant, a constant expression, a conversion with a result that is a constant, or the result value of some built-in functions such as unsafe.Sizeof applied to any value, cap or len applied to some expressions, real and imag applied to a complex constant and complex applied to numeric constants.
(出典) https://golang.org/ref/spec#Constants

要するに、定数は

  • 各種リテラル
  • 定数を示す識別子
  • constant expression(const a = 2(2021/3/2訂正)1+2みたいな表現)
  • len(a)みたいなある種の組み込み関数

などで表せるもので、リテラルはその中の一つにすぎないということです。

conversion

いわゆる「型キャスト」のこと。GoSpec本文中ではtype castingではなくconversionという表現に統一されています。
随所にこの単語は出てくるため、意味を把握しておくとスムーズ。

default type

untypedな定数に、型を暗黙的に与える必要が出てきたときに当てがわれる型のことを「そのuntypedな定数がもつdefault type」と表現します。

例えば、i := 0と書かれたときに、識別子iの型は明示的には定義されていません。このときに、iの型について議論しなくてはいけないときには、これは一旦0のdefault typeであるint型と解釈されます。

GoSpec本文中に登場する回数は5回。忘れた頃に出てきて困惑する単語です。

defined type

intstringといった、Goで元々用意されている型と、ユーザーによってtypeを使い定義された型の2つをdefined typeと呼びます。

のびしーさん(@shino_nobishii)が作られたこちらの資料で非常にわかりやすく説明されているため、詳細はそちらに譲ります。
https://docs.google.com/presentation/d/1JSsrv404ZDJSnxp4UcJ3iVYDJAiT3RyETMe9AvbkxuY/edit#slide=id.p

dynamic type

インターフェース型の変数に、その中に代入された値にしたがって実行時に与えられる動的な型のことをdynamic typeという。
型アサーションを説明する章で重要になり、多用される概念です。

対義語はstatic type。

expression

式。評価された結果、値(value)になるもののことをexpressionと表現される。

例えばa[x]という表現は、「配列orスライスax番目の値」などと解釈でき、値を得ることができるため、これはIndex expressionsという名前がついている。

identical<->different

2つの型が同じか/違うかどうかという概念のこと。

例えば、以下の型定義を見ると

type MyInt = int	// identical
type MyString string	// different

MyIntは型エイリアスなのでint型と同一(identical)、MyStringは新しくこの名前の型を宣言しているのでstring型とは違う(different)型となります。

conversion(型キャスト)やassignable(代入可能性)を定めるのに型の同一性(type identity)は重要な概念となります。
詳細な定義に関してはGoSpec本文を参照してください。

また、syumaiさん(@__syumai)が書いた記事もとてもわかりやすいのでここで紹介します。
https://zenn.dev/syumai/articles/77bc12aca9b654

interface

インターフェース型だけじゃなくて、インターフェースに付属するメソッドセットのこともinterfaceと呼ぶことがあります。

(例文) An interface type specifies a method set called its interface.

numeric

直訳は数値。
GoSpec本文中では「rune, int, float, complex」の型をまとめてnumeric typeと呼ぶことが多く、何回も出てくるワードです。

(2021/3/21加筆修正)
GoSpec本文中では「rune, int, float, complex, byte」の型をまとめてnumeric typeと呼ぶことが多く、何回も出てくるワードです。
また、「rune, int, float, complex」の型の定数もまとめてnumeric constantsと呼ばれます。

predeclared identifier

直訳すると「宣言済みの識別子」。

予約語(keyword)とは別の概念になります。例えばiotatrueはpredeclared identifierですがkeywordではありません。
predeclared identifierのリストはGoSpec本文を参照してください。

struct typeの節にのみに登場する概念。

例えば、MyStruct型の構造体が

type MyStruct {
	Ownfield int
	EmbeddedMyStruct
}

のようにEmbeddedMyStruct型の埋め込みフィールドを持っていたとします。
このとき、EmbeddedMyStruct型のフィールドorメソッドfが、MyStruct型の正当なセレクタになるのであれば、Mystruct型の変数xからx.fという風に直接これを呼び出すことができるようになります。
このとき、フィールドorメソッドfがpromotedされたと表現されます。

representable(representability)

定数xが型Tでrepresentableであるとは、ざっくりいうと「型Tで表現可能な値の範囲に入っている」ということです。

例えば、1000int8ではrepresentableではありませんが、int16ではrepresentableです。

representableの詳細な定義はGoSpec本文を参照のこと。

selector

構造体フィールドやメソッドを表す表現x.ffの部分のことを「セレクタ」と呼びます。

signature

関数における「引数と返り値のセット」のことをGoSpec本文中ではsignatureと表現しています。

(例文) Such a declaration provides the signature for a function implemented outside Go, such as an assembly routine.

statement

文。ifforといった、値を返さない表現のこと。

static type

dynamic typeに対して、直接定義された型や、newを使って与えられた静的な型をstatic typeと呼びます。

(例文) The static type (or just type) of a variable is the type given in its declaration, the type provided in the new call or composite literal, or the type of an element of a structured variable.

underlying array

スライスの内部実装の中には、「ある配列へのポインタ」と「ある配列の中で参照できる部分の長さ(=len)」と「ある配列の長さ(=cap)」が含まれています。
その「ある配列」のことをunderlying arrayと表現しています。

詳しくは以下のGo Blogをご覧ください。
https://blog.golang.org/slices-intro

underlying type

Goでは全ての型にunderlying typeというものが定義されていて、それがtype identityやassignableの定義設定に大いに関わってきます。

DQNEO(@DQNEO)さんがまとめたこちらのスライドがわかりやすいので、詳細説明はそちらに譲ります。
https://speakerdeck.com/dqneo/go-language-underlying-type

unique(uniqueness)

ある識別子がunique(一意)であるかどうかというのはきちんとUniqueness of identifiersという節で定義されています。
いわゆる「構造体の中に同じ名前のフィールドやメソッドがあってはいけませんよ」という決まりの「同じ」という部分の正確な定義を行っているということです。

(例文) In a method set, each method must have a unique non-blank method name.

untyped

型が明確に宣言されていない定数のことをuntypedと呼びます。

例えば、以下の2つの定数のうち、Piはfloat型と明示されているのでtypedですが、zeroはこの時点ではuntypedです。

const Pi float64 = 3.14159265358979323846
const zero = 0.0         // untyped floating-point constant

そのため、定数zeroの型の解釈が必要になった場合は、default typeが割り当てられることになります。

DQNEO(@DQNEO)さんがまとめたこちらのスライドもわかりやすいです。
https://speakerdeck.com/dqneo/go-specification-untyped-constants

プログラム全般について

Goだけではなくて、IT関係の英文を読むときに一般によく出てきそうな単語をまとめました。

argument

値のある実引数のことをGoSpec本文ではargumentと表す傾向にあります。

(例文) A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments: make(chan int, 100)

例文でいうと、make関数の第2引数は、すでに100という実値が与えられているのでargumentとなったのだと思います。

assignment

名詞で「代入」の意。

(例文) Strings can be concatenated using the + operator or the += assignment operator.

base

n進数の基数のこと。

(例文) An optional prefix sets a non-decimal base: 0b or 0B for binary, 0, 0o, or 0O for octal, and 0x or 0X for hexadecimal.

また、後に出てくるbase prefixは、2進数であることを表す0bや8進数を表す0oといった接頭辞を示します。

byte-wise

「1byteごとに」という意味。

(例文) String values are comparable and ordered, lexically byte-wise.

compatibility

互換性。特に、backward compatibilityで後方互換性という意味になります。

concurrent programming

並行処理のこと。

(例文) Go has explicit support for concurrent programming.

decimal

「小数の」という意味も存在しますが、GoSpec本文中では「10進数の」という意味で用いられています。

(例文) A single 0 is considered a decimal zero.

implementation-specific

「実装依存」の意。

(例文) There is a predeclared numeric types with implementation-specific sizes: uint either 32 or 64 bits

indices

indexの複数形。
某ネット英和辞書を参照したら、単数形indiceは古い表現と書いてありましたが、複数形は健在のようです。

"index"の単語のほうがGoSpec本文中には多く出てくるのですが、"indice"もたまに出てきてびっくりします。

operand

オペランドのこと。日本語にすると「被演算子」。

例えば1+2という式があったときに、+が演算子で12がオペランド(被演算子)となります。

(例文) The comparison operators == and != must be fully defined for operands of the key type.

parameter

決まった値がまだ与えられていない仮引数に用いられる傾向にある言葉です。

(例文) A function type denotes the set of all functions with the same parameter and result types.

例文の場合、「関数型は同じ引数と返り値をもつ全ての関数を表すことができる」という内容で、ここでの引数に具体的な値はまだ想定されていません。そのため、parameterが使われているのかと思います。

parenthesize

「括弧にいれる」という動詞。

(例文) Parameter and result lists are always parenthesized except that if there is exactly one unnamed result it may be written as an unparenthesized type.

pass by value

「値渡し」することをこう表現します。

(例文) The parameters of the call are passed by value.

pointer indirection

indirectionは間接参照の意味です。pointer indirectionはポインタの間接参照、つまりポインタ変数をデリファレンスして値を得る操作のことを指します。

(例文) For an operand x of pointer type *T, the pointer indirection *x denotes the variable of type T pointed to by x.

portability

「移植性」という名詞。

(例文) To avoid portability issues all numeric types are defined types and thus distinct except byte, which is an alias for uint8, and rune, which is an alias for int32.

precision

数値の精度。int型やfloat型を何bitで表すか?という話です。

(例文) Although numeric constants have arbitrary precision in the language, a compiler may implement them using an internal representation with limited precision.

property

直訳すると「属性」。jsonや構造体のプロパティを見るとわかるように、何かにつける名前のようなものです。

(例文) Programs are constructed from packages, whose properties allow efficient management of dependencies.

筆者は「パッケージのプロパティって何やねん!?」となりました。ここでいうとパッケージをそれと識別するためのパッケージ名ですね。

radix, mantissa

それぞれ浮動小数点の基数、仮数のこと。

recursively

「再帰的に」という副詞。

(例文) An interface type T may not embed itself or any interface type that embeds T, recursively.

result value

「戻り値」の意。

(例文) Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is.

two's complement arithmetic

2の補数表現のこと。

unary, binary

それぞれ「単一項の」「二項の」という意味の形容詞です。
GoSpec中では主に引数・戻り値の数を言及するときに出てくることが多いです。

(例文) Primary expressions are the operands for unary and binary expressions.

uniform pseudo-random

「一様擬似乱数」という名詞。

variadic

「(関数が)可変長引数の」という形容詞です。

(例文) A function with such a parameter is called variadic.

文字的なこと

Unicodeの「文字」に関する英単語です。

canonicalized

Unicode文字には正規化という概念が存在し、それを表す単語です。

(例文) The text is not canonicalized, so a single accented code point is distinct from the same character constructed from combining an accent and a letter.

例文では「Goではソースコードの正規化は行わない」と記述されています。つまり、「â」1文字と、「a+ ̂」の2文字の組み合わせでâにしたものは別物として扱うということです。日本語話者ではわからないよこんなの。

upper/lower case

それぞれ「大文字」「小文字」の意。

(例文) The first character of the identifier's name is a Unicode upper case letter (Unicode class "Lu");

英語辞書

ただの英単語帳です。大学受験とかで使えそう

allocate

動詞で「割り当てる」。メモリ割り当て関連の話で頻出する単語です。

(例文) A slice created with make always allocates a new, hidden array to which the returned slice value refers.

correspond

「一致する」だけではなく「相応する、相当する」という意味もあり、後者がGoSpecではよく出てきます。

(例文) If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself.

denote

「示す」という動詞。めちゃくちゃ出てくる。死ぬほど出てくる。

(例文) A type may be denoted by a type name.

dividend, divisor

それぞれ「割られる数」「割る数」という意味です。

embed

「埋め込む」という動詞。
GoSpec本文だと、構造体の埋め込みフィールド周りの話でよく出てきます。

(例文) Further rules apply to structs containing embedded fields, as described in the section on struct types.

inhabit

「ファイルを同じディレクトリ内にいれる」というのをinhabitと表現するんですね。

(例文) An implementation may require that all source files for a package inhabit the same directory.

innermost

「最も内側の」という形容詞。

(例文) The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.

invoke/ invocation

動詞で「呼び出す」の意。名詞形にするとinvocation。

(例文) A function literal can be assigned to a variable or invoked directly.

leftmost

「一番左の」という形容詞。

(例文) The <- operator associates with the leftmost chan possible:

それぞれ直訳すると「合法な」「違法な」という形容詞。
illegalは「やってはいけない書き方」を表す単語としてGoSpec本文中に頻出します。

(例文) It is illegal to define a label that is never used.

parentheses, square brackets, or curly braces

それぞれ(), [], {}のカッコを表します。

respective

各自の、それぞれという形容詞。

(例文) The types of the elements and keys must be assignable to the respective field, element, and key types of the literal type

retrieve

「検索する」という動詞。
add-retrieve-removeの三拍子の中ではあまり出てこないので忘れがち。

(例文) Elements may be added during execution using assignments and retrieved with index expressions;

stand for

表す、象徴するという熟語。

(例文) If present, each name stands for one item (parameter or result) of the specified type and all non-blank names in the signature must be unique.

successive

「連続する」という形容詞。
"success"は「成功」という意味だけではないというのが英語的ポイントです。

(例文) Within a constant declaration, the predeclared identifier iota represents successive untyped integer constants.

take O as argument

「引数をとる」の動詞はtakeを使うんですね。

(例文) A new, empty map value is made using the built-in function make, which takes the map type and an optional capacity hint as arguments.

take the type

英語では型はtakeするものだそうです。

(例文) If the type is present, all constants take the type specified

valid <-> invalid

直訳するとそれぞれ「有効」「無効」という形容詞です。
これも、ある書き方が正しい記述方式かどうか、ということを議論する文脈で用いられることが多いです。

(例文) The type assertion is invalid since it is not possible for x to store a value of type T.

yield

「産出する」という動詞。

(例文) The expression yields a function equivalent to Mv but with an explicit receiver as its first argument;

まとめ

以上、「The Go Programming Language Specificationを読もうぜ!」という布教文章でした。

ちなみに筆者は#gospecreadingというイベントに2~3回参加して、そのときの知識内容をもとに書いただけの素人です。
プロの方で追記修正して欲しいことがあれば、コメントいただければ対応します。記事の内容がより厚くなるようなご指摘は大歓迎ですのでどしどし教えてください。

脚注
  1. Go QuizについてはGoクイズ Advent Calendar 2020がまとまったコンテンツとして存在します。 ↩︎

  2. Alternativeは直訳が「選択肢」です。つまり、|で区切ることで「A or B」とするときの「A」や「B」を選択肢とみている故の単語チョイスです。 ↩︎

Discussion

DQNEODQNEO

「コンパイラがこのEBNFに基づいて構文解析をしています」

厳密にいうと、EBNFに基づいて構文解析をするのは原理的にできないんですよね。
最大の理由は、構文解析を全部完了した後じゃないと手に入らない情報がEBNFに登場しているからです。

代表的な例が TypeName です。
ある識別子 "X" が TypeNameかどうかは構文解析を全部終わった後じゃないと決められないケースがあるのですが、(例えば type X int がファイル末尾や別ファイルにある場合)
EBNF 上は CompositeLit 中に TypeName が登場してしまってるので、構文解析器はEBNF通りには構文解析できないという矛盾があります。

なので、構文解析器はEBNF通りに動いてるわけではないです。
別の言葉でいうと、構文解析器はEBNFで定義された文法の部分集合しかパースできないということになります。

さき(H.Saki)さき(H.Saki)

なるほど、

ある識別子 "X" が TypeNameかどうかは構文解析を全部終わった後じゃないと決められないケースがあるのですが、(例えば type X int がファイル末尾や別ファイルにある場合)

この例え、すごくわかりやすいです。
そうなると「(構文解析での)構文木の作成アルゴリズムで、EBNFを元にしているところはあくまで一部」という理解でよろしいでしょうか?

DQNEODQNEO

EBNFと言語仕様書の文章を見ながら構文解析を実装しているとは思いますが、100% EBNFどおりにはなっていない、というところですかね。

DQNEODQNEO

unary, binary は、&i 1+2 とかの単項式、二項式で使われる単語なので、特に引数とは関係のない概念ですね

magurotunamagurotuna

そのままキーワードと訳すなかれ。

とありますが、一般的には予約語とキーワードは必ずしも一致するわけではないと思うので、"Keyword" は「キーワード」と訳すほうが良いのではないでしょうか?少なくとも「訳すなかれ」と言いきって良いほどではないのかな?と感じました。
Go 固有の事情には明るくないため、「Goだと keyword = reserved word と考えても差し支えないよ」というニュアンスでの記載でしたら無視してください🙏

concurrent programming
並列処理のこと。

"concurrent" は「並行」と訳すのが良いのでは、と思います。
"concurrent" と "parallel" が別であることは Concurrency is not parallelism - The Go Blog にも書かれている通りです。
オライリーの Concurrency in Go の日本語訳版の書名が Go言語による並行処理 になっていることなどから、「並行」という訳語をあてるのがベターかなと思いました。

DQNEODQNEO

[]で囲うと、中身を0回または1回繰り返すことを示す

[] は中身がオプションつまり省略可能であるという意味なので、特に繰り返し(Repeat) という意図はないかなと思います。"Repetition" がまさに繰り返しなので。

DQNEODQNEO

constant expression(const a = 2みたいな表現)

constant expression は、 2 + 3 みたいな定数を組み合わせた式のことです。

const a = 2 は 定数宣言 であって、その中の右辺の 2 が定数式です。

Ryuji IwataRyuji Iwata

numericにて、numeric typeの型に「rune」が含まれているので(あれば)「byte」も追加されておくと正確かと。(参考:GoSpec - Numeric types

さき(H.Saki)さき(H.Saki)

ご指摘ありがとうございます!

修正してて気づいたんですが、GoSpec Constantsのところには

Rune, integer, floating-point, and complex constants are collectively called numeric constants.
(訳)runeとintとfloatと複素数が数値定数ですよ

と書いてあるんですね。numeric typeに含まれるbyteがnumeric constantsに含まれないのがちょっと不思議です。

Ryuji IwataRyuji Iwata

runeもbyteもコード的には同じような型エイリアスですが、

type byte = uint8
type rune = int32

他に差があると言えば、GoSpecではruneにはリテラルの説明があるが(Rune literals)byteにはないところあたりで、何か扱いに違いがあるのかもですね。見直しありがとうGoざいました!q@w@p

jiftechnifyjiftechnify

Go言語仕様独特の用語について網羅的にまとまっており、非常に参考になりました。執筆ありがとうございます。
重箱の隅をつつくようですが、訳語について一つ気になった部分があったので指摘させていただきます。

ある識別子がunique(同一)であるかどうか

unique の訳語はここでは「一意」とするのが妥当ではないでしょうか。
「同一」だと identical のほうに近い意味になってしまうと思います。

さき(H.Saki)さき(H.Saki)

unique の訳語はここでは「一意」とするのが妥当ではないでしょうか。
同一」だと identical のほうに近い意味になってしまうと思います。

確かに「一意」のほうが良さそうですね。
uniqueとidenticalの違いについて考えるいいきっかけになりました。コメントありがとうございました!