goa のトップレベル DSL
goa の design は DSL をネストさせながら記述しますが、その一番上の階層に来るものをトップレベル DSL と呼びます。標準で用意されている apidsl では以下の DSL がそれに当たります。
API()
Resource()
Type()
MediaType()
前提として、すべての DSL は Go の関数であり、それを用いて記述される design は Go のソースコードです。
トップレベル DSL の記述
goa ではトップレベル DSL を以下のように記述します。
var _ = API("cellar", func() { // Definitions. })
なぜ以下のように書けないのでしょうか?
API("cellar", func() { // Definitions. })
Go の言語仕様
まず Go の言語仕様を確認します。
Go の言語仕様 に ソースファイル構成 という仕様が明記されています。それによるとソースファイルは以下の 3 つのブロックから構成されます。
- ひとつのパッケージ句 (Package clause)
package
- 任意の数のインポート宣言 (Import declarations)
import
- 任意の数のトップレベル宣言 (Top level declarations)
この「トップレベル宣言」に含まれるのは以下の宣言です。
- 定数
const
- 型
type
- 変数
var
- 関数・メソッド
func
Go のコードとして見た API()
DSL
翻って API()
DSL を Go のコードとして見てみましょう。
var _ = API("cellar", func() { // Definitions. })
上記の DSL は大きく以下の 3 つに分割することができます。
API()
という関数の呼び出し (Calls)var
による変数宣言 (Variable declarations)=
による代入 (Assignments)
総合するとこのコードは「代入を伴った変数宣言」であり、前述した Go のトップレベル宣言として有効です。
小ネタ
本来 var
による変数宣言では型の指定が必要ですが、ここでは代入を伴っているため型推論が働いています。もちろん以下のように明示的に型の指定を行うこともできます。
var _ *design.APIDefinition = API("cellar", func() { // Definitions. })
ブランク識別子への代入がないとどうなるか
もう以下の書き方がなぜ駄目なのかわかりますね。
API("cellar", func() { // Definitions. })
API()
は関数呼び出しであり Go のトップレベル宣言として記述することはできないのです。
結論
Go の言語仕様的にトップレベルに書けるのは以下のいずれかの宣言であるため、代入を伴った変数宣言とする必要があるから。
- 定数
const
- 型
type
- 変数
var
- 関数・メソッド
func