Go Annoyances
borud.no
borud.no
If you need to explain at length what the parameter should conform to, it may need to be sub-typed so it may validate its own complex constraints on construction, or in Go 1.18, it could potentially be generic and constrained.
Would be very interested if the author could clarify the need for structured parameter documentation with some examples.
I certainly dislike stuff such as:
// Basename returns the last portion of the path (that is, everything
// after the last / or \, depending on the platform).
//
// @param path string The pathname to modify.
// @return string The modified pathname.
func Basename(path string) string
Which almost invariable ends up happening; the usual way this happens is something along the lines of "there is not enough documentation!", "I know, let's add a linter to enforce it!", useless documentation ensues.Writing stuff as prose rather than a "bullet-list" in general is also better, IMO. I often find "structured" documentation like this much more sloppy in practice, as too often people will just slap on some @-things and confuse that for "documentation". Writing one or two paragraphs is often clearer.
[0]: https://www.sethvargo.com/what-id-like-to-see-in-go-2/
Associated HN discussion: https://news.ycombinator.com/item?id=30205232
The "don't copy range values" one is particularly indicative. I think the whole point of copying range's values is to avoid all the ugly, ugly problems with modifying the object being iterated over if you don't copy them. Wishing for real references to the iterated-upon object in a range is one of those things that you should maybe think about before asking the genie for it.
I find this kind of critizism to be unfounded as it seems the author have not actually used the feature but is just complaining about it.
This is how it is used, and as you see there is no preference for "the broken US practice of illogical month/day ordering":
n := time.Now()
localTimeFormat := "2006-01-02 15:04:05"
formatted := n.Format(localTimeFormat)1) Month, 2) Day of Month, 3) Hour, 4) Minute, 5) Second, 6) Year
So yes, you DO need to remember that the numbers are 'month/day' ordered, in order to, from memory, derive that it's 2006-01-02, and not 2006-02-01. With strftime, we effectively standardised "%Y-%m-%d"
Nothing to do with Google here, by the way. It's just the default format of the Unix date command.
So if you can't remember the order, just switch to your shell and run `LC_ALL=C date`.
LC_ALL=C date => Sun Feb 13 14:27:18 GMT 2022
I once religiously wrote Doxygen (aka javadoc) comments for all my C code. Eventually I realized it was just very verbose red tape that added little value. In C no-one bothers to actually generate or read the generated Doxygen HTML output. It's just waste. Granted in Java this is not the case as javadoc is more permeated into the culture. Anywho, personally I'm glad the Go designers went with something more lightweight that focuses on the essentials. All just personal preferences...
WRT logging, can it be done right? I think in most circumstances the right answer is to not log anything at all. Logging poisons the code you are writing, forcing the user of your code to relate to the same logging APIs as you choose. Maybe they will have 10s, 100s of different logging APIs to relate to in the final program. If you are writing reusable code for others I think the answer is to avoid logging to the largest extent possible. Otherwise do something really simple that the user of your code can control if they want or not.
I’m not a Go dev so I only have a fuzzy view into the actual dev experience. But I would never have imagined that you’re expected to make struct types public in order to return instances. That’s a baseline expectation of hiding implementation details right? Along with logging levels, I’m shocked that this is more painful than in typical JS/TS setups.
This is what I think most TypeScript libraries should be doing, but every bit of inertia makes it unlikely. In all honesty I think type systems should be designed around contracts/protocols and make concrete types private by default. Sure it’s a lot of ceremony, but it’s less ceremony than human communication in emails and issue threads to clarify that undocumented features are undocumented on purpose.
You can return unexported ("private") instances; for example this is perfectly valid:
type unexport struct {
Str string
}
func (unexport) Method() string { return "export" }
func NewUnexport() unexport {
return unexport{Str: "asd"}
}
And you can get/set the exported Str field from another package without problems, as well as call the exported Method.You don't even need to make an unexport package type and can use an anonymous struct or type declared in the function too, although you can't add any methods like this and is often cumbersome because you need to declare the anonymous struct more than one.
And, of course, you can make parts of a struct private by just not exporting them. It's really not all that different to how OOP languages work with public/private, except that visibility is dictated in the name itself.