Notes on the Go translation of Reposurgeon (2020)
1–10 of 155 posts
Re: Notes on the Go translation of Reposurgeon (2020)
#2Respect for his early contributions, but he’s basically what PG would be if he had not become obscenely rich; rehashing old points, relying on history to have a reputation, contributing little novel output.
He sure can complain about things he has no intention to put effort into improving though.
Re: Notes on the Go translation of Reposurgeon (2020)
#3Re: Notes on the Go translation of Reposurgeon (2020)
#4e.g.
func myFunc(cx context.Context) {
ctx, cancel = context.WithCancel(cx)
defer cancel()
for item := range iterator(ctx) {
}
}Re: Notes on the Go translation of Reposurgeon (2020)
#5Regarding your point on iterators, I typically use a channel with context; this lets you exit that range loop early and will automatically stop the related go routine. (assuming it’s context aware) e.g. func myFunc(cx context.Context) { ctx, cancel = context.WithCancel(cx) defer cancel() for item := range iterator(ctx) { } }
As a rule of thumb, the amount of work transferred by any concurrency primitive should significantly exceed the cost of the concurrency primitive itself. I do have a couple of uses of this pattern where what is on the other side of the channel is something reading off a network and parsing lines of JSON into internal structs, in which case the overhead of the channel isn't necessarily too bad. (In one case, it even chunks the lines of JSON into a slice of several parsed structs, reducing channel overhead even more.) But it's a terrible solution in general; iterating over an array and doing any sort of very fast "thing" to each element that only costs a handful of assembler instructions, a very common use case, has terrible overhead.
It's a real pity, because the semantics of that solution are pretty close to the right answer. But it's a huge performance trap. Something as basic as iteration needs to not be a huge performance trap.
[1]: Relative to Go, anyhow. I haven't timed it directly but I wouldn't be surprised that a channel-based iterator like that would be comparable to Python's general iteration speed, or at least not off by a very large factor. It's just that "Python's normal level of performance" is "atrocious Go performance".
Re: Notes on the Go translation of Reposurgeon (2020)
#6I would also add that Enums + Exhaustive Switch is a very very weak area in Go that would really benefit the language a ton. I've used those features in other languages and that's one of the things I miss the most, especially when dealing with a ton of web API's that have a defined set of values for properties.
Able to have the confidence that we're checking for every situation that could occur on an enum type across the codebase is one less thing I need to worry about.
Re: Notes on the Go translation of Reposurgeon (2020)
#7Great writeup. Some of the points are moot now that generics are being released, but some very valid concerns. I would also add that Enums + Exhaustive Switch is a very very weak area in Go that would really benefit the language a ton. I've used those features in other languages and that's one of the things I miss the most, especially when dealing with a ton of web API's that have a defined set of values for properti…
golangci-lint (https://github.com/golangci/golangci-lint) is an absolute must, and includes https://github.com/nishanths/exhaustive which will check this for you.
Re: Notes on the Go translation of Reposurgeon (2020)
#8It should be noted that the writeup is a year old, which (understandably) means that the comments on generics are a little out of date.
Re: Notes on the Go translation of Reposurgeon (2020)
#9The bits about exceptions and typing remind me of an open question I have about Go: to me it mainly looks like a language for well-understood problems. Static typing and the lack of ability to do broad, high-level exception catching seem to me to be well-matched for problems where you already understand the solution pretty well. E.g., a port like this.
But how is it for poorly understood problems? E.g., You're doing a startup where the technical risks are low and the major unknowns are about user needs and the correct experiences to deliver. Or you're doing exploratory work for an art piece.
In those contexts, I've felt much more effective starting with vague/implicit typing and some broad, high-level exception-catching blocks. That lets me avoid a bunch of questions about the right types and structures until a more solid domain vocabulary emerges. At that point you can refactor/rewrite toward clarity. But I don't see a good way to be willfully vague/casual in Go.
Re: Notes on the Go translation of Reposurgeon (2020)
#10Regarding your point on iterators, I typically use a channel with context; this lets you exit that range loop early and will automatically stop the related go routine. (assuming it’s context aware) e.g. func myFunc(cx context.Context) { ctx, cancel = context.WithCancel(cx) defer cancel() for item := range iterator(ctx) { } }
The major problem I have with that is performance is atrocious [1] if the iterator isn't doing something very nontrivial. Since the vast majority of iterators amount to incrementing at most a handful of things and then returning something indexed by those things, you're paying the cost of channel communication per item but not getting that channel cost amortized over any significant costs in the iteration itself. If…
If you need raw performance and you are just doing some minimal operations on a slice then yeah you’d want to use a simple for loop instead.
I typically use this pattern in situations where you have a ton of data coming back where you want to avoid storing all of that data in memory.