Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1361] ファイルの概要

このコミットは、Go言語の公式仕様書である doc/go_spec.txt の記述方法に関する変更です。具体的には、文法記述に用いられていた「Parameterized Extended Backus-Naur Form (PEBNF)」という、より複雑な記述形式を廃止し、よりシンプルで一般的な「Extended Backus-Naur Form (EBNF)」による明示的な記述に置き換えることで、仕様書の可読性と保守性を向上させています。この変更は、Go言語の仕様そのものには影響を与えません。

コミット

このコミットは、Go言語の仕様書における文法記述のスタイルを簡素化することを目的としています。以前採用されていた、文法を記述するための「過度に凝った」方法(Parameterized Extended Backus-Naur Form, PEBNF)を削除し、より明示的で分かりやすい構文記述に置き換えました。これにより、仕様書全体の記述がよりシンプル、明確、かつ短くなりました。この変更は、Go言語の仕様自体には一切影響を与えていません。コミットの作者であるRobert Griesemerは、PEBNFの導入は「今にして思えば(私の)間違いだった」と述べています。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4d23030859398c2a17243c878f0abb98f3074204

元コミット内容

- ripped out excessively fancy way of describing grammar
  in favor of explicit constructs
- simpler, clearer, and shorter overall
- no spec changes

(in retrospect it was just a (my) mistake to put it in in the first place)

R=r
DELTA=55  (13 added, 28 deleted, 14 changed)
OCL=21434
CL=21462

変更の背景

Go言語の初期の仕様書では、文法を記述するために「Parameterized Extended Backus-Naur Form (PEBNF)」という形式が採用されていました。これは、一般的なEBNFに「パラメータ化」の概念を導入し、例えばリスト構造などを抽象的に記述できるようにしたものでした。しかし、コミットメッセージにあるように、作者のRobert Griesemerは、このPEBNFが「過度に凝った (excessively fancy)」方法であり、結果として仕様書の記述を不必要に複雑にしていたと判断しました。

このコミットの背景には、仕様書の可読性と保守性の向上という明確な意図があります。パラメータ化された記述は、特定のパターンを再利用する際には簡潔に見えるかもしれませんが、その抽象度ゆえに、文法の具体的な構造を直感的に理解しにくくする側面がありました。また、PEBNFはEBNFの標準的な拡張ではなく、Go言語の仕様書に特有の記述方法であったため、Go言語の仕様を初めて読む開発者にとっては、まずPEBNFの概念を理解する必要があるという障壁も生じていました。

そのため、Go言語の仕様自体を変更することなく、文法記述のメタ言語をより一般的で理解しやすいEBNFの明示的な形式に戻すことで、仕様書全体の品質を高めることが目的とされました。作者自身が「今にして思えば(私の)間違いだった」と認めているように、これは初期の設計判断の修正であり、より良いドキュメンテーションを目指した改善です。

前提知識の解説

このコミットの変更内容を理解するためには、以下の概念について知っておく必要があります。

1. バッカス・ナウア記法 (BNF: Backus-Naur Form)

BNFは、プログラミング言語や形式言語の文法を記述するために広く用いられるメタ言語です。BNFは、言語の構文規則を「プロダクションルール」と呼ばれる形式で表現します。各ルールは、非終端記号(定義される構文要素)と、それがどのように終端記号(実際の言語要素、例えばキーワードや識別子)や他の非終端記号の組み合わせで構成されるかを示します。

例:

<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
<number> ::= <digit> | <number> <digit>

この例では、<digit>が0から9のいずれかの数字であることを示し、<number>が1つ以上の数字の並びであることを示しています。

2. 拡張バッカス・ナウア記法 (EBNF: Extended Backus-Naur Form)

EBNFは、BNFを拡張し、より簡潔で読みやすい文法記述を可能にしたものです。BNFでは繰り返しやオプションの表現が冗長になりがちでしたが、EBNFでは以下のような記号を導入することで、これを改善しています。

  • |: 選択肢(OR)
  • (): グループ化
  • []: オプション(0回または1回出現)
  • {}: 繰り返し(0回以上出現)
  • "" または '': 終端記号(リテラル)

例 (EBNF):

digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" .
number = digit { digit } .

このEBNFの例は、上記のBNFの例と同じ意味ですが、より簡潔に記述されています。Go言語の仕様書では、このEBNFが標準的に使用されています。

3. Parameterized Extended Backus-Naur Form (PEBNF)

PEBNFは、このコミットで削除された概念であり、一般的なEBNFの標準的な拡張ではありません。Go言語の仕様書で一時的に採用されていた、EBNFに「パラメータ化」の概念を導入しようとした試みです。

PEBNFの主な目的は、文法規則の再利用性を高めることでした。例えば、Go言語の構文には、セミコロンで区切られたリストが様々な場所で出現します(例: 変数宣言のリスト、フィールド宣言のリストなど)。PEBNFでは、このような共通のパターンをパラメータ化されたプロダクションとして定義し、異なる文脈でそのプロダクションを再利用することで、記述の重複を避けることを意図していました。

PEBNFの例 (コミット前の仕様書より):

List<P> = P { ";" P } [ ";" ] .
Decl<P> = P | "(" [ List<P> ] ")" .

ここで、List<P>は型パラメータPを受け取り、Pの要素がセミコロンで区切られたリストを表現します。Decl<P>も同様に、P単体、またはPのリストを括弧で囲んだ形式を表現します。

しかし、このパラメータ化は、文法規則の具体的な構造を隠蔽し、かえって理解を難しくする側面がありました。一般的なEBNFの知識だけでは、これらのパラメータ化された規則を直感的に解釈することが困難であり、PEBNF自体の定義を理解する必要がありました。このコミットは、この「過度に凝った」アプローチを廃止し、より直接的で標準的なEBNFの記述に戻すことで、仕様書のアクセシビリティと明瞭性を向上させました。

4. Go言語の仕様書 (doc/go_spec.txt)

doc/go_spec.txtは、Go言語の構文、セマンティクス、および標準ライブラリの動作を詳細に記述した公式ドキュメントです。Go言語の設計者によって作成され、言語の「真実の源 (source of truth)」として機能します。このファイルは、Go言語のコンパイラやツール、そしてGo言語を使用する開発者にとって、言語の挙動を理解するための最も重要なリファレンスです。

技術的詳細

このコミットの技術的な核心は、Go言語の仕様書における文法記述のメタ言語の変更と、それに伴う具体的な文法規則の書き換えにあります。

1. 文法記述メタ言語の変更

コミット前は、仕様書は文法記述に「Parameterized Extended Backus-Naur Form (PEBNF)」を使用していると明記していました。しかし、このコミットでは、その記述を「Extended Backus-Naur Form (EBNF)」に変更しています。

  • 変更前: The syntax is specified using Parameterized Extended Backus-Naur Form (PEBNF).
  • 変更後: The syntax is specified using Extended Backus-Naur Form (EBNF):

この変更は、単なる名称の変更以上の意味を持ちます。PEBNFは、EBNFに「パラメータ化」の概念を導入することで、文法規則の再利用性を高めようとしたものでした。しかし、これはEBNFの標準的な拡張ではなく、Go言語の仕様書に特有の記述方法でした。結果として、この「パラメータ化」の概念を理解するための追加の学習コストが生じ、仕様書の可読性を損ねていました。

このコミットは、このPEBNFの概念を完全に削除し、より一般的で広く認知されているEBNFの記法に統一することで、仕様書をより理解しやすくすることを目指しました。

2. パラメータ化されたプロダクションの削除と明示的な記述への置き換え

PEBNFの削除に伴い、仕様書内で定義され使用されていたパラメータ化されたプロダクション(例: List<P>, Decl<P>) が削除され、それらが表現していた具体的な構文がEBNFの明示的な記述に置き換えられました。

削除されたPEBNFの定義:

--- a/doc/go_spec.txt
+++ b/doc/go_spec.txt
@@ -358,30 +358,26 @@ language support.
 Notation
 ----
 
-The syntax is specified using Parameterized Extended Backus-Naur Form (PEBNF).\n-Specifically, productions are expressions constructed from terms and the\n-following operators:\n+The syntax is specified using Extended Backus-Naur Form (EBNF):\n 
-- |   separates alternatives (least binding strength)\n-- ()  groups\n-- []  specifies an option (0 or 1 times)\n-- {}  specifies repetition (0 to n times)\n-\n-The syntax of PEBNF can be expressed in itself:\n-\n-\tProduction = production_name [ Parameters ] "=" Expression .\n-\tParameters = "<" production_name { "," production_name } ">" .\n+\tProduction = production_name "=" Expression .\n \tExpression = Alternative { "|" Alternative } .\n \tAlternative = Term { Term } .\n-\tTerm = production_name [ Arguments ] | token [ "..." token ] | Group | Option | Repetition .\n-\tArguments = "<" Expression { "," Expression } ">" .\n+\tTerm = production_name | token [ "..." token ] | Group | Option | Repetition .\n \tGroup = "(" Expression ")" .\n \tOption = "[" Expression ")" .\n \tRepetition = "{" Expression "}" .\

この変更により、PEBNFのメタ構文に関する説明が削除され、EBNFの基本的な記号の説明のみが残されました。

さらに、Decl<P>List<P>といったパラメータ化されたプロダクションが使用されていた箇所が、具体的なEBNFの記述に展開されました。例えば、宣言(ConstDecl, TypeDecl, VarDecl)や構造体(StructType)、インターフェース(InterfaceType)、インポート(ImportDecl)の構文は、以前はDecl<P>List<P>を介して記述されていましたが、このコミットにより、それぞれの宣言形式が直接EBNFで記述されるようになりました。

この変更は、文法記述の冗長性を増す可能性はありますが、その代わりに、各構文要素の構造がより明確になり、抽象的な概念を介さずに直接的に理解できるようになります。これにより、Go言語の仕様書は、EBNFの標準的な知識を持つ開発者にとって、よりアクセスしやすく、読みやすいものとなりました。

3. 言語仕様への影響

最も重要な点として、このコミットはGo言語の「仕様そのもの」には一切変更を加えていません。変更されたのは、その仕様を記述するための「方法」だけです。Go言語の構文やセマンティクス、キーワード、型システムなどに変更はなく、既存のGoプログラムの動作に影響を与えることはありません。これは、ドキュメンテーションの改善であり、言語の進化とは別の側面での品質向上です。

コアとなるコードの変更箇所

このコミットで変更されたファイルは doc/go_spec.txt のみです。

主な変更点は以下の通りです。

  1. 日付の更新: - (December 16, 2008) から + (December 17, 2008) へ変更。

  2. 文法記述メタ言語の変更: PEBNFに関する説明が削除され、EBNFに関する説明に置き換えられました。

    --- a/doc/go_spec.txt
    +++ b/doc/go_spec.txt
    @@ -358,30 +358,26 @@ language support.
     Notation
     ----
     
    -The syntax is specified using Parameterized Extended Backus-Naur Form (PEBNF).\n-Specifically, productions are expressions constructed from terms and the\n-following operators:\n    +The syntax is specified using Extended Backus-Naur Form (EBNF):\n     
    -- |   separates alternatives (least binding strength)\n-- ()  groups\n-- []  specifies an option (0 or 1 times)\n-- {}  specifies repetition (0 to n times)\n-\n    -The syntax of PEBNF can be expressed in itself:\n    -\n    -\tProduction = production_name [ Parameters ] "=" Expression .\n    -\tParameters = "<" production_name { "," production_name } ">" .\n    +\tProduction = production_name "=" Expression .\n     \tExpression = Alternative { "|" Alternative } .\n     \tAlternative = Term { Term } .\n    -\tTerm = production_name [ Arguments ] | token [ "..." token ] | Group | Option | Repetition .\n    -\tArguments = "<" Expression { "," Expression } ">" .\n    +\tTerm = production_name | token [ "..." token ] | Group | Option | Repetition .\n     \tGroup = "(" Expression ")" .\n     \tOption = "[" Expression ")" .\n     \tRepetition = "{" Expression "}" .\
    

    PEBNFの自己記述的な文法定義(Production = production_name [ Parameters ] ...など)が削除され、EBNFの基本的な定義に置き換えられました。

  3. パラメータ化されたプロダクションの削除と明示的なEBNFへの展開: Decl<P>List<P>といったパラメータ化されたプロダクションの定義と、それらを使用していた箇所が削除され、具体的なEBNFの記述に置き換えられました。

    • 宣言一般の記述 (Decl<P>List<P>) の削除:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -684,14 +671,6 @@ function, method) and specifies properties of that entity such as its type.\n         		[ "export" | "package" ]\n         		( ConstDecl | TypeDecl | VarDecl | FunctionDecl | MethodDecl ) .\n         		\n        -Except for function, method and abbreviated variable declarations (using ":="),\n        -all declarations follow the same pattern. There is either a single declaration\n        -of the form P, or an optional semicolon-separated list of declarations of the\n        -form P surrounded by parentheses:\n        -\n        -\tDecl<P> = P | "(" [ List<P> ] ")" .\n        -\tList<P> = P { ";" P } [ ";" ] .\
      
    • ConstDecl (定数宣言) の変更:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -796,7 +775,8 @@ Const declarations\n         A constant declaration binds an identifier to the value of a constant\n         expression (§Constant expressions).\n         \n        -\tConstDecl = "const" Decl<ConstSpec> .\n        +\tConstDecl = "const" ( ConstSpec | "(" [ ConstSpecList ] ")" ) .\n        +\tConstSpecList = ConstSpec { ";" ConstSpec } [ ";" ] .\
       \tConstSpec = IdentifierList [ CompleteType ] [ "=" ExpressionList ] .\
       \n         \tIdentifierList = identifier { "," identifier } .\
      
    • TypeDecl (型宣言) の変更:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -949,7 +929,8 @@ Type declarations\n         A type declaration specifies a new type and binds an identifier to it.\n         The identifier is called the ``type name''; it denotes the type.\n         \n        -\tTypeDecl = "type" Decl<TypeSpec> .\n        +\tTypeDecl = "type" ( TypeSpec | "(" [ TypeSpecList ] ")" ) .\n        +\tTypeSpecList = TypeSpec { ";" TypeSpec } [ ";" ] .\
       \tTypeSpec = identifier Type .\
       \n         A struct or interface type may be forward-declared (§Struct types,\
      
    • VarDecl (変数宣言) の変更:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -983,7 +964,8 @@ The variable type must be a complete type (§Types).\n         In some forms of declaration the type of the initial value defines the type\n         of the variable.\n         \n        -\tVarDecl = "var" Decl<VarSpec> .\n        +\tVarDecl = "var" ( VarSpec | "(" [ VarSpecList ] ")" ) .\n        +\tVarSpecList = VarSpec { ";" VarSpec } [ ";" ] .\
       \tVarSpec = IdentifierList ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) .\
       \n         \tvar i int\
      
    • StructType (構造体型) の変更:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -1344,7 +1326,8 @@ an identifier and type for each field. Within a struct type no field\n         identifier may be declared twice and all field types must be complete\n         types (§Types).\n         \n        -\tStructType = "struct" [ "{" [ List<FieldDecl> ] "}" ] .\n        +\tStructType = "struct" [ "{" [ FieldDeclList ] "}" ] .\n        +\tFieldDeclList = FieldDecl { ";" FieldDecl } [ ";" ] .\
       \tFieldDecl = (IdentifierList CompleteType | TypeName) [ Tag ] .\
       \tTag = string_lit .\
       \n        ```
      
      
    • InterfaceType (インターフェース型) の変更:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -1553,7 +1536,8 @@ Type interfaces may be specified explicitly by interface types.\n         An interface type denotes the set of all types that implement at least\n         the set of methods specified by the interface type, and the value "nil".\n         \n        -\tInterfaceType = "interface" [ "{" [ List<MethodSpec> ] "}" ] .\n        +\tInterfaceType = "interface" [ "{" [ MethodSpecList ] "}" ] .\n        +\tMethodSpecList = MethodSpec { ";" MethodSpec } [ ";" ] .\
       \tMethodSpec = IdentifierList FunctionType .\
       \n         \t// A basic file interface.\
      
    • ImportDecl (インポート宣言) の変更:

      --- a/doc/go_spec.txt
      +++ b/doc/go_spec.txt
      @@ -3235,7 +3219,8 @@ The file must begin with a package clause.\n         A package can gain access to exported items from another package\n         through an import declaration:\n         \n        -\tImportDecl = "import" Decl<ImportSpec> .\n        +\tImportDecl = "import" ( ImportSpec | "(" [ ImportSpecList ] ")" ) .\n        +\tImportSpecList = ImportSpec { ";" ImportSpec } [ ";" ] .\
       \tImportSpec = [ "." | PackageName ] PackageFileName .\
       \n         An import statement makes the exported contents of the named\
      

コアとなるコードの解説

このコミットの核心は、Go言語の仕様書における文法記述の「抽象化」から「明示化」への転換です。

変更前は、Decl<P>List<P>といったパラメータ化されたプロダクションが、様々な種類の宣言やリスト構造を抽象的に表現するために使用されていました。例えば、定数宣言、型宣言、変数宣言はすべてDecl<P>という共通のパターンで記述されていました。

変更前の例 (ConstDecl):

ConstDecl = "const" Decl<ConstSpec> .

この記述は、「ConstDecl"const"キーワードの後にDecl<ConstSpec>が続く」ことを意味します。しかし、Decl<ConstSpec>が具体的に何を意味するのかを理解するためには、さらにDecl<P>List<P>の定義を遡って解釈する必要がありました。

変更後の例 (ConstDecl):

ConstDecl = "const" ( ConstSpec | "(" [ ConstSpecList ] ")" ) .
ConstSpecList = ConstSpec { ";" ConstSpec } [ ";" ] .

変更後では、Decl<ConstSpec>という抽象的な概念を介さずに、ConstDeclの具体的な構文が直接記述されています。

  • ConstSpec: 単一の定数仕様。
  • "(" [ ConstSpecList ] ")": 括弧で囲まれた定数仕様のリスト。
  • ConstSpecList = ConstSpec { ";" ConstSpec } [ ";" ] .: ConstSpecがセミコロンで区切られて複数並び、最後にオプションでセミコロンが付くリスト。

この変更により、ConstDeclの構文が、その定義を見ただけで直感的に理解できるようになりました。同様の変更が、TypeDeclVarDeclStructTypeInterfaceTypeImportDeclといった他の主要な構文要素にも適用されています。

このアプローチは、文法記述の行数を増やす可能性がありますが、そのトレードオフとして、以下の利点が得られます。

  1. 可読性の向上: 抽象的なパラメータ化された規則を解釈する必要がなくなり、各構文要素の具体的な構造が直接的に理解できます。
  2. 学習コストの削減: EBNFの標準的な記法のみで文法が記述されるため、Go言語の仕様書を読むためにPEBNFという独自の概念を学ぶ必要がなくなります。
  3. 保守性の向上: 抽象化された規則の変更が、意図しない形で他の構文要素に影響を与えるリスクが低減されます。各構文要素が独立して記述されるため、変更の影響範囲が明確になります。

結果として、このコミットはGo言語の仕様書を、より多くの開発者にとってアクセスしやすく、理解しやすいものにすることに貢献しています。

関連リンク

  • The Go Programming Language Specification: Go言語の公式仕様書。このコミットで変更された doc/go_spec.txt の内容が反映されています。 https://go.dev/ref/spec

参考にした情報源リンク