[インデックス 15201] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)における型変換(Conversion)の構文に関する記述を修正するものです。具体的には、曖昧な型変換にのみ括弧が必要であるという現状の動作を明文化し、以前の「実装上の制限」という記述を削除しています。
コミット
commit 71c941b6f6c133dec3c632b373eaae458eb055e7
Author: Russ Cox <rsc@golang.org>
Date: Mon Feb 11 07:48:14 2013 -0500
spec: only require parens around ambiguous conversions
This is documenting the status quo. The previous cleanup
added this language as an implementation restriction, but
at least for now it is really part of the language proper.
Fixes #4605.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/7305071
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/71c941b6f6c133dec3c632b373eaae458eb055e7
元コミット内容
このコミットの目的は、Go言語の仕様書において、型変換の際に括弧が必要となる条件をより正確に記述することです。以前の記述では、特定の型(ポインタ型、チャネル型、関数型)が変換の対象となる場合に「括弧で囲む必要がある」とされていましたが、これは「実装上の制限」として追加されたものでした。しかし、実際にはこれは言語仕様の一部として扱われるべきであり、特に曖昧さを避けるためにのみ括弧が必要であるという現状の動作を反映させるための変更です。
この変更は、Go言語のIssue #4605 に対応しています。
変更の背景
Go言語の型変換の構文は、特定の状況下で曖昧さを生じる可能性があります。例えば、ポインタ型やチャネル型、あるいは引数を持たない関数型を変換しようとする場合、その構文が型変換なのか、それとも別の演算(例:ポインタのデリファレンス、チャネルからの受信、関数呼び出し)なのかが曖昧になることがあります。
この曖昧さを解消するために、Go言語の初期の仕様では、これらの型を変換する際には常に括弧を付けることが求められていました。しかし、これは一部のケースで不必要に冗長な記述を強いることにもなり、また、コンパイラの実装によっては曖昧さがない場合に括弧なしでも解釈できるケースが存在しました。
このコミットは、Go言語のIssue #4605 で議論された内容を反映しています。このIssueでは、型変換における括弧の必要性について、より柔軟な解釈を求める声が上がっていました。具体的には、構文が曖昧でない場合には括弧を省略できるようにすべきではないか、という点が議論の焦点でした。
このコミットは、その議論の結果として、Go言語の仕様書が「曖昧な場合にのみ括弧が必要である」という、より実用的なルールを明文化するように修正されたものです。これにより、開発者は不必要な括弧を省略できるようになり、コードの可読性が向上することが期待されます。同時に、コンパイラの実装もこの新しい仕様に沿って、曖昧でないケースでは括弧なしの型変換を許容するようになります。
前提知識の解説
Go言語の型変換 (Type Conversions)
Go言語における型変換は、ある型の値を別の型に変換する操作です。これは、異なる型のデータ間で互換性を持たせるために不可欠な機能です。Go言語の型変換は、C言語のような暗黙的な型変換とは異なり、明示的に記述する必要があります。
基本的な構文は以下の通りです。
T(x)
ここで T
は変換先の型、x
は変換される値です。
例:
var i int = 42
var f float64 = float64(i) // intからfloat64への変換
var s string = string(rune(i)) // intからrune、そしてstringへの変換
構文の曖昧性 (Syntactic Ambiguity)
プログラミング言語において、同じ構文が複数の異なる意味に解釈されうる状況を「構文の曖昧性」と呼びます。Go言語の型変換においても、特定の型(ポインタ型、チャネル型、関数型)を変換する際に、この曖昧性が発生する可能性があります。
例えば、*Point(p)
という記述があった場合、これは *(Point(p))
(p
を Point
型に変換し、その結果をデリファレンスする)と解釈することもできますし、(*Point)(p)
(p
を *Point
型に変換する)と解釈することもできます。Go言語のパーサーは、このような曖昧さを解決するために特定のルールを必要とします。
Go言語の仕様書 (Go Language Specification)
Go言語の仕様書は、Go言語の構文、セマンティクス、標準ライブラリの動作などを厳密に定義した公式ドキュメントです。Go言語のコンパイラやツールは、この仕様書に基づいて実装されます。仕様書はGo言語の設計思想と哲学を反映しており、言語の安定性と互換性を保証する上で非常に重要な役割を果たします。
このコミットが修正しているのは、この仕様書の一部である doc/go_spec.html
です。
技術的詳細
このコミットは、Go言語の仕様書における「Conversions」のセクションを修正しています。特に、型変換の際に括弧が必要となる条件に関する記述が変更されています。
変更前の仕様では、以下のように記述されていました。
If the type starts with the operator
*
or<-
, or the keywordfunc
, it must be parenthesized:
これは、変換先の型がポインタ (*
)、チャネル (<-
)、または関数 (func
) で始まる場合、常に括弧で囲む必要があることを示していました。
しかし、このコミットによって、この記述は以下のように変更されました。
If the type starts with the operator
*
or<-
, or if the type starts with the keywordfunc
and has no result list, it must be parenthesized when necessary to avoid ambiguity:
この変更のポイントは以下の通りです。
- 「曖昧さを避けるために必要な場合のみ」 (
when necessary to avoid ambiguity
) という条件が追加されました。これは、常に括弧が必要なのではなく、構文が曖昧になる場合にのみ括弧が必要であることを明確にしています。 - 関数型に関する条件の明確化: 以前は単に
func
で始まる型とされていましたが、新しい記述では「func
キーワードで始まり、かつ結果リスト(戻り値)がない場合」に限定されています。これは、func() int
のような戻り値を持つ関数型は、func() int(x)
のように記述しても曖昧さがないため、括弧が不要であることを示唆しています。
この変更は、Go言語のパーサーがどのように型変換の構文を解釈するかという、より深いレベルのルールを反映しています。パーサーは、入力されたコードを解析する際に、最も長い有効なトークン列を優先的に解釈しようとします。この「最長一致」の原則が、特定の状況で曖昧さを生み出す原因となります。
例えば、func()(x)
という記述は、func()
という関数シグネチャと、それに続く (x)
という引数リストを持つ関数宣言(または関数呼び出し)と解釈される可能性があります。しかし、x
を func()
型に変換したい場合は、(func())(x)
のように括弧で囲むことで、パーサーに (func())
が一つの型であることを明示する必要があります。
一方で、func() int(x)
のような戻り値を持つ関数型の場合、func() int
は明確に型として認識され、その後に続く (x)
は型変換の対象となる値として解釈されるため、曖昧さが生じません。
このコミットは、このようなパーサーの挙動と、それによって生じる構文の曖昧さを、Go言語の仕様書に正確に反映させることを目的としています。これにより、開発者はより直感的に、かつ効率的にGoコードを記述できるようになります。
また、以前の仕様書にあった以下の「実装上の制限」に関する記述が削除されています。
Implementation restriction: For backward-compatibility with the Go 1 language specification, a compiler may accept non-parenthesized literal function types in conversions where the syntax is unambiguous.
この記述は、Go 1の仕様との後方互換性のために、コンパイラが曖昧でない場合には括弧なしの関数型リテラルを許容する可能性がある、というものでした。このコミットによって、この挙動が言語仕様の一部として明文化されたため、この「実装上の制限」という記述は不要になりました。これは、Go言語の設計が成熟し、初期の「実装上の制限」が正式な言語ルールとして昇格したことを示しています。
コアとなるコードの変更箇所
変更は doc/go_spec.html
ファイルに集中しています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -3448,16 +3448,20 @@ Conversion = Type "(" Expression [ "," ] ")" .
<p>
If the type starts with the operator <code>*</code> or <code><-</code>,
-or the keyword <code>func</code>, it must be parenthesized:
+or if the type starts with the keyword <code>func</code>
+and has no result list, it must be parenthesized when
+necessary to avoid ambiguity:
</p>
<pre>
*Point(p) // same as *(Point(p))
-(*Point)(p) // p is converted to (*Point)
+(*Point)(p) // p is converted to *Point
<-chan int(c) // same as <-(chan int(c))
-(<-chan int)(c) // c is converted to (<-chan int)
+(<-chan int)(c) // c is converted to <-chan int
func()(x) // function signature func() x
-(func())(x) // x is converted to (func())
+(func())(x) // x is converted to func()
+(func() int)(x) // x is converted to func() int
+func() int(x) // x is converted to func() int (unambiguous)
</pre>
<p>
@@ -3553,12 +3557,6 @@ implements this functionality under
restricted circumstances.
</p>
-<p>
-Implementation restriction: For backward-compatibility with the Go 1 language
-specification, a compiler may accept non-parenthesized literal function types
-in conversions where the syntax is unambiguous.
-</p>
-
<h4>Conversions between numeric types</h4>
コアとなるコードの解説
このdiffは、Go言語の仕様書における型変換のルールを更新しています。
-
ルール記述の変更:
- 変更前: 「型が
*
または<-
演算子、あるいはfunc
キーワードで始まる場合、括弧で囲む必要がある」 - 変更後: 「型が
*
または<-
演算子で始まる場合、あるいは型がfunc
キーワードで始まり、かつ結果リスト(戻り値)がない場合、曖昧さを避けるために必要な場合にのみ括弧で囲む必要がある」 この変更により、関数型に関する条件がより厳密になり、かつ「曖昧な場合のみ」という条件が追加されたことで、不必要な括弧の強制がなくなりました。
- 変更前: 「型が
-
例の修正と追加:
(*Point)(p)
のコメントがp is converted to (*Point)
からp is converted to *Point
に変更されました。これは、*Point
が型であることをより明確に示しています。(<-chan int)(c)
のコメントも同様にc is converted to (<-chan int)
からc is converted to <-chan int
に変更されました。func()(x)
の例は、関数シグネチャとして解釈されることを示しています。(func())(x)
のコメントがx is converted to (func())
からx is converted to func()
に変更されました。これも、func()
が型であることを明確にしています。+(func() int)(x)
と+func() int(x)
の2つの新しい例が追加されました。これらは、戻り値を持つ関数型の場合、括弧があってもなくても曖昧さがないため、どちらの形式も有効であることを示しています。特にfunc() int(x)
は、括弧なしでも曖昧さがない例として追加されています。
-
「実装上の制限」の削除:
- 以前の仕様書にあった「Go 1言語仕様との後方互換性のために、コンパイラは構文が曖昧でない型変換において、括弧なしの関数型リテラルを受け入れる可能性がある」という段落が完全に削除されました。これは、この挙動がもはや「実装上の制限」ではなく、正式な言語仕様の一部として昇格したことを意味します。
これらの変更は、Go言語の型変換のルールをより正確に、かつ実用的に定義し、開発者がコードを記述する際の混乱を減らすことを目的としています。
関連リンク
- Go言語のIssue #4605: https://code.google.com/p/go/issues/detail?id=4605 (古いGoogle Codeのリンクですが、当時の議論の参照元です)
- Go言語の変更リスト (CL) 7305071: https://golang.org/cl/7305071
参考にした情報源リンク
- Go Language Specification (現在の最新版): https://go.dev/ref/spec
- Go言語の型変換に関する議論 (Issue #4605の内容を理解するために、当時のGoコミュニティの議論や関連するブログ記事などを参照しました。)
- Go言語のパーサーの挙動に関する一般的な知識。
- Go言語の公式ドキュメント。