Spectro: Adventures in Go
markcrossfield.co.uk
markcrossfield.co.uk
What is there to like about this feature?
1. It introduces unavoidable noise into performance testing
2. It makes your functions impure
3. Your simple script will have different output across successive runs, even with the same input
4. It removes a reasonable and useful feature that all other languages support, namely, that you can iterate the same map twice in a row in the same order
5. Working around this usually involves sorting, which in turn requires a lot of boilerplate: https://gobyexample.com/sorting-by-functions
It seems all this does is make life harder. I'd be interested to hear from anyone who thinks this feature has helped them.
2. Impure?
3. That's the point. It's to force people to stop using maps as if they're sorted.
4. Why would you want to rely on the arbitrary nature of a hash table for order?
5. That's the best type of boilerplate. It's short. It brings clarity. You might not need custom sort functions. The sort package supports basic types, which you'd use if you're keeping track of an index with []int for instance. https://golang.org/pkg/sort/
Disclaimer: I've never used this feature myself and I sympathize with the desire on the part of the Go designers to avoid accidental de facto stabilization of hash table iteration order--though it sounds like it could have been done without drawbacks by randomizing at process start.
Can you provide one? (I'm genuinely curious.)
But from a philosophical perspective, referential transparency is a big one. We should prefer to make functions referentially transparent, because it makes them easier to reason about. Sometimes we do not, because it would be too slow or too awkward. But in this case, the loss is gratuitous.
Or to put it concretely: in Python (and every other language), we have the fact that `dict.keys() == dict.keys()`. In Go, this is true sometimes. Doesn't that seem weird to you?
Committing to supporting any specific sorted order iteration would ultimately come with a performance penalty rather than the reverse.
In fact, this strategy is being introduced to several other aspects of golang's runtime like the test execution order and goroutine scheduler (currently only enabled in -race mode).
Go is fairly serious about language stability and they want to hold onto some guarantees for the remainder of Go 1.x. In order to be able to achieve this, they need to be careful about what explicit features they commit to as much as what implicit features people end up depending on. If changing an implemention of a datastructure changes the implicit order of how keys are stored and the entire community has grown to depend on the original undocument order as a feature... then that's a big problem.
> Are you coming from a C++ background?
I don't think it particularly matters in this case, but the majority of my programming career so far was spent in Python. :)
If you're not going to have guaranteed order, then explicitly breaking it to avoid accidental dependency that will break your programs later when the language implementation changes is very much a good thing.
Compare this to python, which has deterministic dictionary order, but it changes across implementations. That's just begging for problems.
I think the whole philosophy of Go is kind of "try to avoid shit breaking in weird and unexpected a mysterious ways", an approach I become more sympathetic to the longer I program.
Generating one seed at startup would solve the problem of accidental de facto stabilization of dictionary iteration order while avoiding the issues mentioned in the parent comment.
range init's the hashmap iterator: https://github.com/golang/go/blob/6327e8dc69c47d6355e2623cc4...
hashmap iterator init sets a random starts: https://github.com/golang/go/blob/master/src/runtime/hashmap...
For example, say you want to benchmark a function that extracts the keys from a map into a slice, and then sorts it via quicksort. Quicksort's performance is affected by the initial ordering, so different runs of the function will behave differently. Was this run really faster, or did I just get lucky with the order this time?
fmt.Fprintf(os.Stderr, "%"+strconv.FormatInt(int64(paddingWidth), 10)+"s %s\r", "", legend)
Which could be improved greatly, and made a lot more readable with: fmt.Fprint(os.Stderr, strings.Repeat(" ", paddingWidth), legend+"\r")
In my experience with Go over the last few years, any time I have this "ugh" moments it pays to take a step back. It's usually a code smell that there's a better way. fmt.Fprintf(os.Stderr, "%*s %s\r", paddingWidth, "", legend)
I'd further wonder whether a separate padding calculation is needed at all (too lazy to go read the source on Github at the moment).