On Go And Using Comments For Annotations
It’s a little strange that Go doesn’t have first-class language features for annotations: arbitrary bits of non-executing metadata that you can add to the code base. There are struct tags, yes, but they’re only applicable for fields on a struct type. Nothing analogous really exists for functions, or even just files. And yet there seems to be a need for something to declare build flags, go generate commands, and lint opt-outs.
The approach taken for this are well-formed comment tags, like //go:generate
, but this seems like a design smell to me. Comments should be limited to the realm of the developer, and the language should stay away from using it for their purpose. Why? Because the language designer can set the syntax. The Go language doesn’t need to be syntactically compatible with anything other than itself, so there’s nothing really stopping the developers from adding dedicated syntax for such a purpose.
Java behaved in a similar way before annotations were added and it was pretty hacky. You’re left adding non-standard constructs to the Javadoc and relying on a code parser to extract that. When Java annotation where added, it became the responsibility of the compiler to take on that role, and annotations was available in the reflection package. You could walk the type hierarchy to find annotations for fields and methods without having to resort to parsing. Go offers something similar for struct tags, but nothing really for functions or files.
And they probably don’t need to. While the Java annotation features were powerful, it came at the cost of complexity. The tags need to be well-defined, including whether or not they should be made available at runtime. I’m not saying Go needs all that. Since the “well-formed comment tag” approach is little more than just a convention, it means anyone can make use of it with little more than a sentence saying “set this tag like this.”
So the sweet spot would be to keep this light-handed approach to tags, while having some dedicated syntax for it. Maybe something similar to preprocessors? You could have a tag begin with a hash and have arbitrary “arguments” which, like struct tags, will be taken as a single string the tag implementor will need unpack:
#go:build javascript
package main
import (
"embed"
"log"
)
#go:generate make-mocks -mode annoying
#go:embed ./myfiles
var files embed.FS
func main() {
log.Println("I like hashes")
}
Not sure it’s entirely better than the comments — or maybe my eyes are not used to such constructs. And it probably wouldn’t work for all cases, such as embedded C code when using “cgo”. But I do like that they’re not comments. Their purpose is absolutely clear.
Anyway, just a passing thought.