Stumbling Into the Golang Spec and Trying to Make Sense of It
I am not sure whether this is worth reading, because the Go specification is good and self-contained. Anyway, I’m writing this. The Go language specification describes a general standard for the language itself and describes the syntax using Wirth syntax notation , a variant of EBNF . So I want to learn about the interface type in Go, and the first thing I want to know is how it is formed within a program, and I headed to Go Programming Language Specification .
And I see:
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem = MethodElem | TypeElem .
MethodElem = MethodName Signature .
MethodName = identifier .
TypeElem = TypeTerm { "|" TypeTerm } .
TypeTerm = Type | UnderlyingType .
UnderlyingType = "~" Type .
And one may wonder, what are these weird things?! These, my friend, are rules that say what is an interface and what is not. Let’s start with InterfaceType
. This is a starting rule, and yes, each line here is a rule that is composed to finally make InterfaceType
.
Let’s see the first line:
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
This defines how the entire interface block is formed within a Go file, from this we can infer that every interface block starts with the "interface"
keyword,
this, my friend,
"<anything>"
is a literal string, the exact text within""
must appear in input.
Followed by a "{"
, which is also a literal string, and we can see a matching closing curly bracket literal string also exists at the end. So now, all we have left is what goes inside the interface { <mystery 🪄> }
. We see curly braces, not literal strings, wrapped around the rule InterfaceElem
and ";"
.
these curly brackets indicate repetition in this notation, specifically 0 to n times, for example
{ "a" }c
can be “ac” or “c” or “a” repeated n number of times followed by a “c”
InterfaceElem
is another rule. Here, we can compose rules together to manage complexity. It is similar to what we’ve seen, plus more operators, which you can reference here
.
Based on this we can now write:
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
Ah yes, this won’t work, because it’s only part of the syntax, the rule itself does not give enough context for the compiler to work with, we need a type name for other things to assert interface restrictions.
Finally!
SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
Declaration = ConstDecl | TypeDecl | VarDecl .
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
TypeDef = identifier [ TypeParameters ] Type .
Type = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeArgs = "[" TypeList [ "," ] "]" .
TypeList = Type { "," Type } .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
SliceType | MapType | ChannelType .
As you can see InterfaceType
the rule we previously explain is composed within TypeDecl
which has the completed syntax we can work with, which the Go compiler gladly take.