Make generated code better match Go conventions, and improve usage ergonomics #83
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The current generated go code has a couple of quirks in it what makes it more difficult to work with than it needs to be. Two primary culprits are:
All fields with structs are always pointers to structs, rather than concrete structs
When providing a API interface to a dataset (such as a Restful API), it's normal for optional fields to be pointers, and non-optional fields to concrete types, including
structs. This approach minimises the number ofnilchecks people have to do, and generally Go discourages the usage of pointers unless they're actually needed because you want to share a value.Making every
structvalue in the generated code is very unergonomic, as it either requiresnilchecks littered at every level of your code that interacts with thesestructs, or (more likely) people don't bother with thenilchecks, relying on Pkl to enforce the existence of values. However that approach means there's no indication as to what might break if a non-optional field is later make optional. By following go conventions, this change will result in build failures, rather the current runtime failures.Structs sometimes have an
Implpostfix and sometimes don'tCurrently the generated code will create an
Interfacefor everyopenorabstractPkl class, and name the associatedstructSomthingImpl. There's nothing inherently wrong with this approach, but it does make it unnecessarily painful to change a Pkl class from closed to open, or visa-versa.If a class is made
openthen the existing Gostructwill haveImplappended to its name, and its existing name will become aninterface, breaking all existing code that previously referenced the originalstruct. There's no need for this breakage as there is also a Go convention to prefix interfaces withI(although frowned upon, but no more than appendingImpltostructs). Using the interface prefix, rather than thestructpostfix, means that the names ofstructs remains static regardless of the modifiers on the Pkl class. Thus when a Pkl class is madeopena newISomethinginterface is created, but the existingSomthingstructremains untouched, preventing unnecessary breakage.Example of changes
These changes are probably best viewed by looking at the diffs of the snippet test outputs, but here's an example that demonstrates most the resulting changes to the generated go code.
Original:
New: