An unordered list of things I miss in Go
The blog post highlights features missing in Go, such as ordered maps, default arguments, and improved nullability, suggesting these enhancements could benefit the language despite requiring significant revisions.
Read original articleThe blog post discusses various features that the author misses in the Go programming language, highlighting areas for potential improvement. The author appreciates Go's qualities but believes it could benefit from enhancements. Key points include the absence of ordered maps in the standard library, which would allow for consistent iteration order based on insertion. The author notes that while third-party implementations exist, having such a feature in the standard library would reduce friction for developers. Another point raised is the lack of keyword and default arguments in function declarations, which could simplify function calls and improve API design. The author cites Python's handling of default arguments as a beneficial feature that Go currently lacks. Lastly, the post addresses the issue of nullability, suggesting that while Go's use of zero values mitigates some problems associated with nil values, the language could still benefit from a more robust approach to nullability, similar to what is seen in other languages. The author concludes that some of these changes would require significant revisions to the language.
- The author misses ordered maps in Go's standard library for consistent iteration.
- Default and keyword arguments in function declarations are seen as beneficial features missing in Go.
- The author highlights the need for improved handling of nullability in Go.
- The post suggests that some proposed changes would require major revisions to the language.
Related
Common Interface Mistakes in Go
The article delves into interface mistakes in Go programming, stressing understanding of behavior-driven, concise interfaces. It warns against excessive, non-specific interfaces and offers guidance from industry experts for improvement.
I Probably Hate Writing Code in Your Favorite Language
The author critiques popular programming languages like Python and Java, favoring Elixir and Haskell for immutability and functional programming benefits. They emphasize personal language preferences for hobby projects, not sparking conflict.
Dysfunctional Options Pattern in Go
The Functional Options Pattern in Go is compared to the Dysfunctional Options Pattern for configuring APIs. The latter, resembling the builder pattern, offers a simpler and faster alternative with method chaining.
Go structs are copied on assignment (and other things about Go I'd missed)
The author reflects on learning Go, highlighting misconceptions about struct assignment, sub-slice modifications, and method receivers. They recommend the "100 Go Mistakes" resource and other learning materials for improvement.
Go structs are copied on assignment
The author shares insights on learning Go, emphasizing struct assignment, slice appending, and value versus pointer receivers. They recommend the "100 Go Mistakes" resource and other tools for better understanding.
Rust's `HashMap` and `HashSet` also do the same with the default hasher (you can plug your own deterministic hasher though). The reason for this choice, which I think also applies to Go, is to be resistant against HashDOS attacks, which can lead to hashmap lookups becoming O(n) on average. This is especially important for maps that will contain data coming from untrusted sources, like data submitted in a GET/POST request.
I do agree though that an ordered map would nicely solve the issue.
Even if they didn't randomize, unless they also explicitly guaranteed stable map order across versions, you DO need to sort the map if you want reproducibility.
Because if you relied on it being conveniently stable within the same Go version, with no guarantees, your program would still be broken (reproducibility wise), if they changed the hashmap implementation.
I think what the author requires is iterating over a sorted list of keys. That is pretty easy to implement using the standard library, and imposes the performance penalty only when it is needed.
I write with the following rule: if a pointer is passed, it shouldn't be nil. If it might be nil, code it as an Optional<> instead.
Un-golike but works great.
Haha well, fun fact, Java did this as well after a bunch of code was broken by a JDK upgrade which changed HashMap iteration order that programmers had been relying upon. Java does at least have ordered maps in the standard lib though. IMO it is a questionable decision to spend CPU resources on randomization in order to mollycoddle programmers with a flawed understanding of an API like this, but then again I'm not the one who gets the backlash when their stuff breaks.
Also, on the subject of nullability, while JSR305 may be considered dead, there's still pretty active work on the Java nullability question both from the angle of tooling (https://www.infoq.com/news/2024/08/jspecify-java-nullability...) and language design (https://openjdk.org/jeps/8316779).
In the rare cases I do want a default it's usually reasonable to just add a second function that calls the first with the default value.
I don't end up doing this a lot, but I certainly have in a couple handful of cases. A lot of Go libraries for HTTP related activities do this with the default context. They'll have a function that accepts a context and a function that has the default context.
Example
https://github.com/slack-go/slack/blob/242df4614edb261e5f4f4...
Honestly, with good naming, I think this is just generally more readable and expectable behavior only takes three lines of code.
First, assuming that the keys iterate in the order they're inserted, the cliche problem.
Second, marshalling JSON and unconsciously relying on the order in the JSON as hidden semantics. This makes it hard to understand the JSON as a human, as well as making what ought to be a portable format with other languages hard to reuse.
I've decided that Python is in the wrong here, not technically, but rather for encouraging humans to assume too much.
This would get close to python try/catch with even lighter syntax.
This would cut so much boilerplate hand carrying error up the stack.
- then goes on to describe some of the problems sum types would solve. Why. Why doesn’t go need this? It was just presented as a blanket statement without a reason.
Personally, I see missing sum types as a major gap, and I reach for them all the time in other languages.
Go's philosophy is that a coherent, curated feature set is as valid an approach to language design as the C++/Python/... approach of adding every possible language feature.
In particular I doubt Go will ever add null-safety - given the above philosophy, the language's pervasive use of "zero values", and its strong commitment to backwards compatibility.
In Java, objects are responsible for their equals/hashCode implementations. The contract they must abide by is:
1. If two objects are equal, they must produce the same hash code; and
2. If they are not equal, they may produce the same hash code.
So if you had a list of 10 Strings and put them in a map in Java, it's likely you'll get a deterministic order for iterating over them unless you added a random factor. That factor could be a random seed tied to the map that you XOR the hash code with.
You can't really change the hash code itself to avoid a Hash DoS attack because you might break that contract. So how does Go (and Rust?) deal with that? Is Go adding a random seed to each hash map? If not, what is it doing?
As for nullability, there's no going back once you use a type system that expresses nullability.
Lastly, PHP arrays are incredibly convenient, ignoring the weirdness with them being array and hash map hybrids. But th ekey aspect is that they maintain insertion order when you use them like a map. This is so often what you want. Yes, other langauges do this too (eg Java's LinkedHashMap) but it's (IMHO) such a useful default.
- Keyword and default arguments for functions
- Nullability (or nillability)
- Ordered maps in standard library
It's was a play on the hash map iteration.My wishlist is:
1. Add the ? operator is a short hand for if err != nil { return ..., err }
2. Allow member functions to have generic parameters (in addition to the struct parameters)
There's several things that keep me on Go, single binary, decent built in tooling, and decent speed.
I've started playing around with tinygo on Pi Pico's, and after the dealing with getting C and C++ onto other MCUs it's a breath of fresh air.
But the rough edges are very rough. At some point another language is going to come along with better syntax with the same single binary and good tooling and I'll probably switch over as fast as possible.
The specific examples don’t ring true to me though.
Instead of named arguments, use the functional optional pattern.
An ordered map can be implemented as a slice with a get function. If you want to stamp out some code duplication, Go has generics and iterators these days.
What I really miss are enums. Specifically, the ability to map two value sets to each other and get a compile time error if the mapping becomes stale due to a new value.
And other assorted stuff. Like better nil handling, the weird time and http client libraries, nil interfaces and slices, and so on.
Honestly, be explicit rather than hoping for implicit behavior.
It is one of the rules now I live by. I don't have to worry about leaving an argument during calling anymore.
I think it fits well with the pythonic mantra of explicit is better than implicit.
PHP has all of the features listed in the article. Not saying anyone should choose PHP over Go.
I'm currently using LiteIDE, any good soul could suggest how what plugin to install in Zed, or Lapce, or Pulsar ?
(Unfortunately I refuse to use VS Codium because microsoft, and NeoVim because IBM CUA keybindings)
> func(s *string) {
> // s maybe nil here, better check first
> }
If this happens, you don't have proper checks before this call. Clearly an error check was missed prior to this call.
It's a bit nitpicky, but this always annoys me. Maps/dictionaries are not hashtables. A hashtable is one of the structures you can use to implement a map. A hashtable stores arbitrary items, not key-value associations, and can be used to quickly retrieve an item knowing its hash. If you want to implement a map using a hashtable, you need to also define a type that wraps a key-value pair and handles hashing and equality by only comparing the key.
Also, maps can be implemented with other underlying data structures as well. Java's standard library even offers a built-in TreeMap, which uses a red-black tree to store the pairs instead of HashMap's hashtable.
Related
Common Interface Mistakes in Go
The article delves into interface mistakes in Go programming, stressing understanding of behavior-driven, concise interfaces. It warns against excessive, non-specific interfaces and offers guidance from industry experts for improvement.
I Probably Hate Writing Code in Your Favorite Language
The author critiques popular programming languages like Python and Java, favoring Elixir and Haskell for immutability and functional programming benefits. They emphasize personal language preferences for hobby projects, not sparking conflict.
Dysfunctional Options Pattern in Go
The Functional Options Pattern in Go is compared to the Dysfunctional Options Pattern for configuring APIs. The latter, resembling the builder pattern, offers a simpler and faster alternative with method chaining.
Go structs are copied on assignment (and other things about Go I'd missed)
The author reflects on learning Go, highlighting misconceptions about struct assignment, sub-slice modifications, and method receivers. They recommend the "100 Go Mistakes" resource and other learning materials for improvement.
Go structs are copied on assignment
The author shares insights on learning Go, emphasizing struct assignment, slice appending, and value versus pointer receivers. They recommend the "100 Go Mistakes" resource and other tools for better understanding.