I understand why Go does not have a native `set` type.
As much as I hate to admit; it is because you really do not need it.
When I was a Java main
I would pedantically always used the collection type that was most appropriate semantically correct for the data it was collecting. If I was storing a list that only contained unique items, I would always use a Set
, usually a TreeSet
so I would get insert order iteration. Same for non-unique item collections, I would use a List
instead. This had the advantage of documenting intention to anyone reading the code months later, usually me, so they would get that extra bit of context and information about what and why the data was being used.
When I became a Go main
I chafed at not having all these distinctly semantically rich type safe collections. I chafed at not having type safe collections; I went Go main well before Generics were added.
I searched for the reasoning behind the omission of the set
type as a built in. I mean if you look at the Set
implementation in Java, it is Map
all the way down just like the recommended way of doing it Go.
Except, it "looks ugly" to have to "repeat yourself" to "twist a map into a set" in Go. I searched for Set
implementations, I never found one that really resonated with me and Go.
All of them were not very "Goish". They were either copies of the Java Set
interface, or littered with receiver methods for all the different set
operations, which in Go should be normal functions that just take two sets and do the operation on them.
I will not even get into all the Java/C++ iterator implementations that are so rejected by the Go community and for good reason.
So I build my own
I did what every self respecting, at least until they have done this, programmer does, I wrote a set
implementation that I thought was the most idiomatic Go way of doing a set
.
Then, I never actually used it
That is not entirely true but it is accurate. I did use it, just like I "used" all the ones I found on github.com
, and I ripped them all out shortly after I put them in.
I mean, I used my implementation, until I ripped it out as well. I found that I did not really need to enforce uniqueness in a list of things, often enough to warrant having to look up how this thing worked the few times I used it.
I work with unique lists of things all the time, I just do not need to pedantically enforce the uniqueness at creation and modification, because I found out I really never modified them after creation.
I created them once and just referred to them many times. Think enums and looking them up by string
. These cases end up being both rare enough and similar enough to in a common way to just create a "Live Template" in GoLand to generate the boilerplate for their creation on a keystroke.
No "AI" generation required. If I am actually needing to process some data dynamically where uniqueness needs to be guaranteed as the data is added to a container, it is going to have space and or time requirements for me to use some numerical library that does this for me anyway.
It took a few years to finally come to this realization
I did not really come to this realization that quickly. It took a few years and thousands of lines of Go and dozens of applications, servers, cli, desktop, etc, for this to reach this epiphany.
I still want a proper set
type in Go.
But, I do not want it as a module you import. Not for silly reasons about "too many dependencies". It is about it being a "first class citizen" as a Go built in just like slice
, map
and channel
. The syntax I want is:
s := make(set[string],0,7)
s = append(s, \"Sunday\")
// you get the idea
s = append(s, \"Saturday\")
I intentionally picked acomparable
type so I can ignore that issue entirely. But,set
without support forstruct
s that do not conform tocomparable
is pretty useless.
What I wanted was a native solution
I want a slice
that has enforced uniqueness, I do not want a map[string]interface{}
, even if I get more functionality out of the map
by being able to associate a value (abbreviation, or whatever) with the unique day of the week name.
It is illogical, but I really like the semantic of a unique array
or slice
being just that, not "keys in a map".
Pedantic reason, I am Autistic, I own it.
I was digging through the Go compiler source map.go
If you look at makemap
, makeslice
and makechan
and it would be fairly easy to add makeset
and update the parser to support a native set
type that is actually a slice
and support setsorted
.
I am not sure it is worth the effort, especially since the non-trivial comparable
issue needs to be solved. I will probably try to add it just as an exercise to get to know the Go runtime source better. I assumed until I saw the source that make()
was some native code or something embedded into the runtime, but it is just plain Go code.