<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wilhelm Murdoch</title>
    <description>The latest articles on DEV Community by Wilhelm Murdoch (@wilhelm-codes).</description>
    <link>https://dev.to/wilhelm-codes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034067%2Ff94aa74b-4be5-4daf-9dec-797c13815677.jpg</url>
      <title>DEV Community: Wilhelm Murdoch</title>
      <link>https://dev.to/wilhelm-codes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wilhelm-codes"/>
    <language>en</language>
    <item>
      <title>It's OK to Let Go</title>
      <dc:creator>Wilhelm Murdoch</dc:creator>
      <pubDate>Fri, 17 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/wilhelm-codes/its-ok-to-let-go-58fk</link>
      <guid>https://dev.to/wilhelm-codes/its-ok-to-let-go-58fk</guid>
      <description>&lt;p&gt;So, I’m a sucker for wordplay and puns. Anyway…&lt;/p&gt;

&lt;p&gt;There’s a line in my shell history that goes back to 2022: &lt;code&gt;go get github.com/wilhelm-murdoch/go-collection&lt;/code&gt;. For years it was the first dependency into almost every Go project I tinkered with, sometimes before I’d even give the idea a solid shape. I’d open Neovim, initialise a module and then my little collection library got pulled in. Just like muscle memory.&lt;/p&gt;

&lt;p&gt;Today I’m archiving it. Not because it broke and not because I got bored of it, but because the Go standard library quietly walked up and did its job better than it ever could. This post is part explanation and part eulogy, because I think we’re generally quite bad at ending software on purpose and it’s worth practising in public.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why even write it in the first place?
&lt;/h2&gt;

&lt;p&gt;Waaaay back in 2022 Go 1.18 had just shipped generics after roughly a decade of the community asking, arguing and writing increasingly unhinged &lt;code&gt;interface{}&lt;/code&gt; workarounds. I wanted to actually &lt;em&gt;learn&lt;/em&gt; the new type parameter machinery rather than just read about it and I was also deeply tired of writing the same &lt;code&gt;for&lt;/code&gt; loop to check whether a slice contained a thing for the hundredth time.&lt;/p&gt;

&lt;p&gt;So &lt;a href="https://github.com/wilhelm-murdoch/go-collection" rel="noopener noreferrer"&gt;go-collection&lt;/a&gt; happened. A single generic &lt;code&gt;Collection[T]&lt;/code&gt; type wrapping a slice, with all the conveniences I kept reaching for: &lt;code&gt;Contains&lt;/code&gt;, &lt;code&gt;Find&lt;/code&gt;, &lt;code&gt;Filter&lt;/code&gt;, &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Sort&lt;/code&gt;, &lt;code&gt;Batch&lt;/code&gt;, &lt;code&gt;Push&lt;/code&gt;, &lt;code&gt;Pop&lt;/code&gt; and a few dozen friends. All chainable, fluent, tested and small.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are far more comprehensive modules out there, but this one works quite well for my purposes. &lt;cite&gt;me, in the README, setting expectations appropriately for once&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence was doing more work than I realised at the time. It wasn’t trying to compete with the big functional-utility libraries. It was scratching a personal itch and for a few years it scratched it well. Then Go’s standard library grew and slurped most of it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking a trip to the morgue.
&lt;/h2&gt;

&lt;p&gt;Cause of death is easy to establish here, because it happened in three well-documented releases. Go 1.21 shipped the &lt;code&gt;slices&lt;/code&gt; package. Go 1.22 and 1.23 finished the job. Let’s walk through it, method by method, like the nerds we are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it have the thing?
&lt;/h3&gt;

&lt;p&gt;The bread and butter. Probably eighty percent of my actual usage of this library was some flavour of “is this thing in there?”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;fruits&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"orange"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"strawberry"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fruits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orange"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// true&lt;/span&gt;

&lt;span class="n"&gt;fruits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"str"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c"&gt;// 2&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of Go 1.21, the standard library does both on a plain slice with no wrapper type in sight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;fruits&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"orange"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"strawberry"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"orange"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// true&lt;/span&gt;

&lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IndexFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"str"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c"&gt;// 2&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;New&lt;/code&gt;, no &lt;code&gt;.Items()&lt;/code&gt; to unwrap at the end when some other API wants a real slice. It’s just my function with better ergonomics and a compiler team maintaining it. I’m fine with this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sorting, where I don’t even get to feel bitter.
&lt;/h3&gt;

&lt;p&gt;Here’s my &lt;code&gt;Sort&lt;/code&gt;. I want you to look at what using it actually required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Index-based comparators, so you had to reach &lt;em&gt;back into the collection you were currently sorting&lt;/em&gt; to get at the values. I inherited that design from the old &lt;code&gt;sort.Slice&lt;/code&gt; idiom and it was clunky then too. The modern equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when you’re sorting something with actual structure, &lt;code&gt;SortFunc&lt;/code&gt; hands you the two elements directly, with &lt;code&gt;cmp.Compare&lt;/code&gt; doing the boring part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SortFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn’t a case of the standard library catching up to my library. It blew my version right out of the water.&lt;/p&gt;

&lt;h3&gt;
  
  
  The killing blow came with iterators.
&lt;/h3&gt;

&lt;p&gt;Go 1.22 added &lt;code&gt;slices.Concat&lt;/code&gt;, which retired my &lt;code&gt;Concat&lt;/code&gt;. Fine, that one was three lines anyway. But Go 1.23 shipped range-over-function iterators and with them &lt;code&gt;slices.Chunk&lt;/code&gt;, and that’s the release where I stopped pretending this was still a contest. &lt;code&gt;Batch&lt;/code&gt; was one of the few methods I was genuinely proud of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// process item as part of batch N&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it’s a language feature wearing a standard library hat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// chunk is a []Record of up to 100 items&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A real &lt;code&gt;for&lt;/code&gt; loop. You can &lt;code&gt;break&lt;/code&gt; out of it, &lt;code&gt;continue&lt;/code&gt; past things and return early. My callback-based version couldn’t do any of that without contorting itself. The trouble is that a wrapper library can only ever be as expressive as the language it wraps and the language just grew.&lt;/p&gt;

&lt;h3&gt;
  
  
  The stuff that never needed a library.
&lt;/h3&gt;

&lt;p&gt;An honest confession while we’re standing over the body: a decent chunk of the API never deserved to exist in the first place. &lt;code&gt;Push&lt;/code&gt;, &lt;code&gt;Pop&lt;/code&gt;, &lt;code&gt;Shift&lt;/code&gt;, &lt;code&gt;Unshift&lt;/code&gt;. I wrote JavaScript for years before Go was ever a public thing and apparently nobody walks away from that unscathed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"e"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// s = append(s, "e")&lt;/span&gt;
&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// item = s[len(s)-1]; s = s[:len(s)-1]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comments on the right are the entire implementation. Idiomatic Go slice handling was already good at this in 2012. Those methods existed because my fingers missed &lt;code&gt;Array.prototype&lt;/code&gt;, not because Go was missing anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  The “survivors”, if you could even call them that.
&lt;/h3&gt;

&lt;p&gt;Here’s the wrinkle that makes this more interesting than a straight obituary. &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Filter&lt;/code&gt;, &lt;code&gt;Reduce&lt;/code&gt;, &lt;code&gt;Some&lt;/code&gt;, &lt;code&gt;None&lt;/code&gt;; the functional stalwarts &lt;em&gt;still&lt;/em&gt; aren’t in the standard library, all these years later. On paper, that’s the corner of the market my little module could have retired to. A niche! Sustained relevance!&lt;/p&gt;

&lt;p&gt;Except, no. First, because my &lt;code&gt;Map&lt;/code&gt; had a secret shame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See it? &lt;code&gt;T&lt;/code&gt; in, &lt;code&gt;T&lt;/code&gt; out. Go methods can’t introduce new type parameters, so a method-based &lt;code&gt;Map&lt;/code&gt; can never change the element type. Strings to strings, ints to ints. A &lt;code&gt;Map&lt;/code&gt; that can only map onto itself is a &lt;code&gt;Map&lt;/code&gt; in vibes only; the moment you want lengths of strings you’re back to writing a loop anyway.&lt;/p&gt;

&lt;p&gt;And second, more fundamentally: the Go community collectively shrugged and decided the loop was fine. The proposal discussions happened, the experiments ran and the ecosystem largely settled on this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ducks&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duck&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;ducks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duck&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four lines. Obvious to anyone who’s read any Go at all, no import, nothing to learn, nothing to maintain and debugger-friendly. Even where &lt;code&gt;go-collection&lt;/code&gt; wasn’t superseded, it turned out to be unnecessary, which is somehow a more thorough defeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Software is a tool, not a legacy.
&lt;/h2&gt;

&lt;p&gt;So that’s the technical half. Here’s the part I actually wanted to write about.&lt;/p&gt;

&lt;p&gt;We don’t really have a culture of &lt;em&gt;finishing&lt;/em&gt; software. A project is either “actively maintained” or it’s “abandoned”, with all the guilt that word drags behind it. Issues accumulate, badges rot and the README slowly becomes a lie because nobody wants to be the person who admits the thing is done, or no longer necessary. I’ve had this module sitting in that limbo for a while now, still getting pulled into new projects out of habit while a strictly better replacement sat in the standard library the whole time.&lt;/p&gt;

&lt;p&gt;But software isn’t a legacy; it’s a tool. Tools serve a purpose. This one’s purpose was to teach me generics in their first year of existence and to spare me a few thousand &lt;code&gt;for&lt;/code&gt; loops while the language sorted itself out. Purpose served on both counts. And of all the ways for a library to die, being absorbed by the standard library is the best one available. It means the gap you filled was real and now it’s just &lt;em&gt;gone&lt;/em&gt;, fixed at the correct layer, for everyone and forever. That’s not a failure state. That’s the mission succeeding so hard the mission ceases to exist.&lt;/p&gt;

&lt;p&gt;Keeping it alive from here would mean maintaining a slightly worse, slightly slower, personally-branded copy of &lt;code&gt;slices&lt;/code&gt; out of pure sentiment. Nobody needs that, least of all me. I’m busy enough and would much rather move on to newer, more interesting things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical bits.
&lt;/h2&gt;

&lt;p&gt;So today, the repo gets the full send-off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A deprecation notice at the top of the README, pointing anyone who lands there at the &lt;a href="https://pkg.go.dev/slices" rel="noopener noreferrer"&gt;slices&lt;/a&gt; package and at this post.&lt;/li&gt;
&lt;li&gt;The repository gets &lt;a href="https://github.com/wilhelm-murdoch/go-collection" rel="noopener noreferrer"&gt;archived&lt;/a&gt; on GitHub. Read-only, clearly signposted and locked in eternal statis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re one of the however-many people with this module in a &lt;code&gt;go.mod&lt;/code&gt; somewhere nothing breaks. The Go module proxy caches published versions more or less permanently, so &lt;code&gt;v1.0.11&lt;/code&gt; will keep resolving long after we’re all dust. Archiving just makes the end of our relationship official rather than leaving it implied.&lt;/p&gt;

&lt;p&gt;If you’ve got a little module of your own in the same situation that has been superseded, purpose spent, kept alive out of habit, then consider this your permission slip. Write the notice, hit archive and go build that next thing. An honest ending is a kindness to your users and to yourself. It’s OK to let go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wilhelm-murdoch/glazier/commit/8b6b84a220225fe714b8623aac1b183d64faf09a" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is an example of me tearing &lt;code&gt;go-collection&lt;/code&gt; out of my very recent Glazier project. This one stung a little at how easily it was replaced.&lt;/p&gt;

&lt;h2&gt;
  
  
  In closing …
&lt;/h2&gt;

&lt;p&gt;Thanks, little buddy. You were there before the ecosystem knew what generics were supposed to look like, you appeared in more of my &lt;code&gt;go.mod&lt;/code&gt; files than any dependency I didn’t write, and you were, right to the end, exactly as comprehensive as I needed you to be.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go get&lt;/code&gt; in peace.&lt;/p&gt;

</description>
      <category>projects</category>
      <category>go</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
