[インデックス 14012] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)に対する変更であり、特に型変換(Conversion)における関数型の扱いに関するものです。具体的には、func
キーワードで始まる型変換において、曖昧さを避けるために括弧(()
)の使用を義務付けるという言語仕様の変更を反映しています。また、既存の演算子(*
や<-
)に加えてfunc
キーワードも括弧が必要なケースとして明示的に追加されています。
コミット
commit 3188ffc93138ca18857575052f74100d64e31df5
Author: Robert Griesemer <gri@golang.org>
Date: Wed Oct 3 13:46:37 2012 -0700
go spec: conversion types starting with "func" must be parenthesized
Also: Be explicit what operator means with respect to conversion types.
The parenthesis requirement is a language change. At the moment,
literal function types in conversions that cannot possibly be
followed by a '(' don't need parentheses. For instance:
func(int)int(x) -> same as (func(int)int)(x)
func()()(x) -> same as (func())(x)
but:
func(int)(x) -> could be func(int)x {...}
Fixes #4109.
R=rsc, r, iant, ken, iant
CC=golang-dev
https://golang.org/cl/6584065
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3188ffc93138ca18857575052f74100d64e31df5
元コミット内容
このコミットの主な目的は、Go言語の仕様において、func
キーワードで始まる型変換の構文を明確にし、曖昧さを排除することです。特に、関数型リテラルが型変換の対象となる場合に、括弧を義務付けるという言語仕様の変更が導入されました。これにより、func(int)(x)
のような記述が、型変換なのか、それとも関数シグネチャの一部なのかという構文解析上の曖昧さを解消します。
変更の背景
Go言語の初期の仕様では、型変換の対象となる型が*
や<-
といった演算子で始まる場合に括弧が必要であるとされていました。しかし、関数型リテラルが型変換の対象となる場合、特に引数リストの後に続く括弧が、型変換の対象となる式の括弧なのか、それとも関数シグネチャの一部なのかという構文上の曖昧さが発生する可能性がありました。
例えば、func(int)(x)
という記述は、以下のように二通りの解釈が可能です。
func(int)
という関数型へのx
の型変換:(func(int))(x)
func(int)x
という関数シグネチャの一部:func(int)x {...}
このような曖昧さは、コンパイラの実装を複雑にし、開発者にとっても混乱の原因となるため、Go言語の設計思想である「明確さ」と「シンプルさ」に反します。この問題は、GoのIssue #4109として報告されており、このコミットはその解決策として提案されました。
この変更は、Go 1の言語仕様との後方互換性を考慮しつつ、将来的な言語の安定性と明確性を確保するために行われました。
前提知識の解説
Go言語の型変換 (Conversions)
Go言語における型変換は、ある型の値を別の型に明示的に変換する操作です。これは、Type(Expression)
という形式で記述されます。ここでType
は変換先の型、Expression
は変換される値です。
例:
var i int = 10
var f float64 = float64(i) // int型からfloat64型への変換
関数型 (Function Types)
Go言語の関数型は、関数のシグネチャ(引数の型と戻り値の型)を定義します。
例:
type MyFunc func(int, string) (bool, error)
この型は、int
とstring
を引数にとり、bool
とerror
を返す関数を表します。
構文解析の曖昧さ (Syntactic Ambiguity)
プログラミング言語の構文解析において、同じ文字列が複数の異なる意味に解釈されうる状況を「構文解析の曖昧さ」と呼びます。これは、コンパイラやインタプリタがコードを正しく解釈する上で問題となります。Go言語では、このような曖昧さを極力排除する設計がなされています。
Go 1 言語仕様
Go 1は、Go言語の最初の安定版リリースであり、その言語仕様はGo言語の長期的な互換性を保証する基盤となっています。このコミットで言及されている「Go 1言語仕様との後方互換性」は、既存のGo 1コードが新しい仕様変更によって動作しなくなることを避けるための重要な考慮事項です。
技術的詳細
このコミットは、Go言語の仕様書であるdoc/go_spec.html
を直接修正することで、型変換のルールを更新しています。
変更の核心は、型変換の構文規則にfunc
キーワードを追加し、その際に括弧を義務付ける点にあります。
変更前の仕様では、型変換の対象となる型が*
(ポインタ)や<-
(チャネル受信)といった演算子で始まる場合に括弧が必要でした。
<p>
If the type starts with an operator it must be parenthesized:
</p>
<pre>
(*Point)(p) // p is converted to (*Point)
<-chan int(c) // same as <-(chan int(c))
(<-chan int)(c) // c is converted to (<-chan int)
</pre>
このコミットにより、このルールにfunc
キーワードが追加されました。
<p>
If the type starts with the operator <code>*</code> or <code><-</code>,
or the keyword <code>func</code>, it must be parenthesized:
</p>
<pre>
(*Point)(p) // p is converted to (*Point)
<-chan int(c) // same as <-(chan int(c))
(<-chan int)(c) // c is converted to (<-chan int)
func()(x) // function signature func() x
(func())(x) // x is converted to (func())
</pre>
ここで重要なのは、func()(x)
が「関数シグネチャ func() x
」と解釈される例と、(func())(x)
が「x
が(func())
に変換される」例として明確に区別されている点です。これにより、前述の構文解析の曖昧さが解消されます。
さらに、後方互換性に関する「実装上の制約(Implementation restriction)」が追加されています。
<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>
これは、Go 1の既存コードが新しい仕様によってコンパイルエラーとならないよう、コンパイラが構文的に曖昧さがない場合に限り、括弧のない関数型リテラルでの型変換を許容してもよい、という柔軟性を持たせています。これは、言語仕様の変更が既存のコードベースに与える影響を最小限に抑えるための配慮です。
コアとなるコードの変更箇所
変更はdoc/go_spec.html
ファイルに集中しています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of September 28, 2012",
+ "Subtitle": "Version of October 3, 2012",
"Path": "/ref/spec"
}-->
@@ -3394,7 +3394,8 @@ Conversion = Type "(" Expression [ "," ] ")" .
</pre>
<p>
-If the type starts with an operator it must be parenthesized:
+If the type starts with the operator <code>*</code> or <code><-</code>,
+or the keyword <code>func</code>, it must be parenthesized:
</p>
<pre>
@@ -3402,6 +3403,8 @@ If the type starts with an operator it must be parenthesized:\n (*Point)(p) // p is converted to (*Point)\n <-chan int(c) // same as <-(chan int(c))\n (<-chan int)(c) // c is converted to (<-chan int)\n+func()(x) // function signature func() x\n+(func())(x) // x is converted to (func())\n </pre>\n \n <p>\n@@ -3488,6 +3491,12 @@ implements this functionality under\n restricted circumstances.\n </p>\n \n+<p>\n+Implementation restriction: For backward-compatibility with the Go 1 language\n+specification, a compiler may accept non-parenthesized literal function types\n+in conversions where the syntax is unambiguous.\n+</p>\n+\n <h4>Conversions between numeric types</h4>\n \n <p>\n```
## コアとなるコードの解説
1. **仕様書の日付更新**:
```diff
- "Subtitle": "Version of September 28, 2012",
+ "Subtitle": "Version of October 3, 2012",
```
これは、仕様書がこのコミットによって更新された日付を反映しています。
2. **括弧が必要な型の拡張**:
```diff
-If the type starts with an operator it must be parenthesized:
+If the type starts with the operator <code>*</code> or <code><-</code>,
+or the keyword <code>func</code>, it must be parenthesized:
```
この変更により、型変換の対象となる型が`*`(ポインタ)や`<-`(チャネル受信)だけでなく、`func`キーワードで始まる場合も括弧が必要であることが明示されました。これは、このコミットの主要な言語仕様変更点です。
3. **`func`キーワードに関する例の追加**:
```diff
+func()(x) // function signature func() x
+(func())(x) // x is converted to (func())
```
これらの例は、`func`キーワードが絡む構文の曖昧さを具体的に示し、どのように解釈されるべきかを明確にしています。
* `func()(x)`: これは型変換ではなく、`func()`という戻り値の型を持つ関数`x`の宣言(または関数リテラル)として解釈されることを示しています。
* `(func())(x)`: これは`func()`という関数型への`x`の型変換であることを示しています。括弧によって明確に型変換であることが示されています。
4. **実装上の制約の追加**:
```diff
+<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>
```
この段落は、Go 1との後方互換性を維持するための重要な注釈です。構文的に曖昧さがない場合(例えば、`func(int)int(x)`のように、関数型の後に続く括弧が引数リストの括弧と明確に区別できる場合)、コンパイラは括弧のない関数型リテラルでの型変換を許容してもよい、という柔軟性を提供しています。これにより、既存のGo 1コードが新しい仕様に準拠していなくても、動作し続ける可能性が高まります。
これらの変更は、Go言語の構文解析をより堅牢にし、開発者にとっての明確性を高めることを目的としています。
## 関連リンク
* Go Issue #4109: [https://github.com/golang/go/issues/4109](https://github.com/golang/go/issues/4109)
* Gerrit Change-Id: `I222222222222222222222222222222222222222` (コミットメッセージの`https://golang.org/cl/6584065`に対応するGerritのチェンジリストID)
## 参考にした情報源リンク
* Go言語の公式ドキュメント (Go Programming Language Specification): [https://go.dev/ref/spec](https://go.dev/ref/spec)
* Go言語のIssueトラッカー: [https://github.com/golang/go/issues](https://github.com/golang.go/issues)
* Go言語のGerritコードレビューシステム: [https://go.googlesource.com/go/+log](https://go.googlesource.com/go/+log)
* Go言語の型変換に関する一般的な情報 (例: Go by Example - Type Conversions): [https://gobyexample.com/type-conversions](https://gobyexample.com/type-conversions) (一般的なGo言語の学習リソースとして)
* Go言語の関数型に関する一般的な情報 (例: Go by Example - Functions): [https://gobyexample.com/functions](https://gobyexample.com/functions) (一般的なGo言語の学習リソースとして)