ADR 0022: Capitalization is visibility; Lyth names are Go names
Status: Accepted (supersedes 0010; amends 0013)
Context
Section titled “Context”ADR 0010 kept Lyth’s own naming: declarations were private unless pub,
struct fields public unless private, and the Go name was the Lyth name with
its first letter changed to match. Go APIs were referenced verbatim, so every
file already mixed the two conventions (r.URL.Query() next to
store.list()). On top of that, the name a programmer wrote was not the name
that existed: pub const defaultLimit was DefaultLimit in Go, and the field
id was Id. Stack traces, pprof, delve, go doc, Go callers, grep, and code
generated by tools all showed the Go spelling. The mechanical capitalization
also produced non-idiomatic Go (Id, UserId) that Go linters flag, and the
initialism heuristic that would fix it was rejected as a guess.
Decision
Section titled “Decision”- Visibility follows Go’s rule. A top-level declaration, method, struct field,
or interface method whose name starts with an uppercase letter is exported;
any other is unexported. There are no
puborprivatekeywords; writing one is E0008 with a hint naming the declaration’s exported or unexported spelling. - The Go name of a Lyth declaration is its Lyth name. The one exception is an
unexported name that is a Go keyword or predeclared identifier, which gets a
trailing
_(fun string()isstring_in Go). - Exported struct fields get an automatic
jsonkey in lower camel case: the leading uppercase run is lowered, keeping the last letter of an initialism that starts the next word (Name→name,ID→id,UserID→userID,HTTPPort→httpPort). An explicitjsonkey is used verbatim, as before. Unexported fields are not serialized, as in Go. - Diagnostics speak of exported and unexported names, and suggest the
renamed spelling (
rename it to Find to export it).
Alternatives
Section titled “Alternatives”- Keep
pubwith mechanical capitalization (ADR 0010): keeps camelCase Lyth APIs but makes source and binary names differ everywhere a Go tool shows a name, and still leaves two casing styles in every file. pubwith an initialism heuristic (userId→UserID): a guess that fails on names likeIPv4, and one more mapping to learn.- Lower-case aliases for Go APIs: rejected in ADR 0010 for the same reasons as before (collisions, two spellings, copy-paste from Go docs).
- Private fields by default with explicit export: brings back Go’s JSON trap differently; capitalization at least makes the rule visible at the field.
Consequences
Section titled “Consequences”Lyth code reads as Go reads: exported functions, types, fields, and methods are
capitalized (fun NewServer, todo.Todo, t.ID, fun T.String()), and one
casing convention covers Go and Lyth APIs alike. Generated Go is unchanged for
existing programs, which were migrated by renaming every declaration to its
former Go name. Implementing a Go interface needs no keyword: a method named
String is exported. Lower-case fields are invisible to encoding/json and
other reflection-based libraries, Go’s beginner trap; a warning for passing
such a struct to json.Marshal is a possible follow-up. The open question
about initialisms is resolved: programmers write ID themselves.