<rss version="2.0">
  <channel>
    <title>Golang on Leon Mika</title>
    <link>https://lmika.org/categories/golang/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Tue, 07 Apr 2026 07:57:13 +1000</lastBuildDate>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/04/07/sky-an-elminspired-language-that.html</link>
      <pubDate>Tue, 07 Apr 2026 07:57:13 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/04/07/sky-an-elminspired-language-that.html</guid>
      <description>&lt;p&gt;🛠️ &lt;a href=&#34;https://github.com/anzellai/sky&#34;&gt;Sky — an Elm-inspired language that compiles to Go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kind of striking seeing Go becoming the target of other languages. Here&amp;rsquo;s another one. Looks a bit like Haskell to my eyes.&lt;/p&gt;
&lt;p&gt;Via: &lt;a href=&#34;https://news.ycombinator.com/item?id=47662116&#34;&gt;Hacker News&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/04/05/lisette-rust-syntax-go-a.html</link>
      <pubDate>Sun, 05 Apr 2026 07:46:22 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/04/05/lisette-rust-syntax-go-a.html</guid>
      <description>&lt;p&gt;🛠️ &lt;a href=&#34;https://lisette.run/&#34;&gt;Lisette — Rust syntax, Go runtime&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A little language inspired by Rust that compiles to Go&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This looks really interesting.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/03/14/it-looks-like-gos-getting.html</link>
      <pubDate>Sat, 14 Mar 2026 17:33:47 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/14/it-looks-like-gos-getting.html</guid>
      <description>&lt;p&gt;It looks like &lt;a href=&#34;https://github.com/golang/go/issues/62026#issuecomment-4041188661&#34;&gt;Go&amp;rsquo;s getting a UUID package&lt;/a&gt; added to the standard library. Someone&amp;rsquo;s been reading my mind, or at the very least &lt;a href=&#34;https://lmika.org/2023/05/19/i-think-its.html&#34;&gt;reading my&lt;/a&gt; &lt;a href=&#34;https://lmika.org/2025/10/15/argh-why-hasnt-go-got.html&#34;&gt;blog&lt;/a&gt;. 😏&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/03/07/my-coolify-instance-has-been.html</link>
      <pubDate>Sat, 07 Mar 2026 07:23:59 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/07/my-coolify-instance-has-been.html</guid>
      <description>&lt;p&gt;My Coolify instance has been running really slowly over the last few days. I may start looking at alternatives, or maybe go back to using Dokku.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/03/05/go-iterators-dont-have-a.html</link>
      <pubDate>Thu, 05 Mar 2026 21:20:18 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/05/go-iterators-dont-have-a.html</guid>
      <description>&lt;p&gt;Go iterators don&amp;rsquo;t have a built in way to send errors back, so I borrowed something from Haskell and made a &amp;ldquo;maybe&amp;rdquo; type:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Maybe&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;] {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Maybe&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;]) &lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt;() (&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The iterator pushes values wrapped in this, which would have &lt;code&gt;Value&lt;/code&gt; set if one is available, or &lt;code&gt;Err&lt;/code&gt; set if not. The &lt;code&gt;Get()&lt;/code&gt; method provides a convenient way to get both, allowing for patterns that look much like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;consume&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;myIter&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingWithVal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/03/05/had-a-go-heh-at.html</link>
      <pubDate>Thu, 05 Mar 2026 21:13:34 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/03/05/had-a-go-heh-at.html</guid>
      <description>&lt;p&gt;Had a go (heh) at making a Go iterator. It was simpler than I expected, once I got my head around how they work. It&amp;rsquo;s a push model, the iterator pushes value to a yield function, which sends it to &lt;code&gt;range&lt;/code&gt;, and receives a flag on whether to continue. The &lt;a href=&#34;https://pkg.go.dev/iter&#34;&gt;package docs&lt;/a&gt; do a good job explaining this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/02/27/a-bit-frustrating-that-i.html</link>
      <pubDate>Fri, 27 Feb 2026 09:24:29 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/02/27/a-bit-frustrating-that-i.html</guid>
      <description>&lt;p&gt;A bit frustrating that I asked my IDE to rename all instances of a method, and it did around half of them, ignoring the interfaces the type implements. I know Go interfaces are different to Java interfaces, but if the type checker passes already, rename things such that it remains that way.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/01/27/reason-go-should-add-enums.html</link>
      <pubDate>Tue, 27 Jan 2026 09:31:01 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/27/reason-go-should-add-enums.html</guid>
      <description>&lt;p&gt;Reason Go should add enums number 5,132: you&amp;rsquo;re not left up to runtime checks to verify that an enum value is invalid. You can rely on the type system to do that. It&amp;rsquo;s all well and good to implement marshallers and unmarshallers that verify that the input is a valid enum value, but that doesn&amp;rsquo;t stop developers from casting any arbitrary string to your enum type. And then what are you expected to do in your String() method (which doesn&amp;rsquo;t return an error)? Do you panic? Return &amp;ldquo;invalid?&amp;rdquo; Return the string enum value as is, even if it is invalid?&lt;/p&gt;
&lt;p&gt;Making it impossible to even produce invalid types is the whole reason why we have a type system. It&amp;rsquo;s why I prefer Go over an untyped language like Python and JavaScript. So it would be nice if Go supported enums in the type system to get this guarantee.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/01/24/added-my-to-the-other.html</link>
      <pubDate>Sat, 24 Jan 2026 06:43:25 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/24/added-my-to-the-other.html</guid>
      <description>&lt;p&gt;Added my 👍 to the other 192 on this &lt;a href=&#34;https://github.com/golang/go/issues/77273&#34;&gt;Go proposal to add generics to methods&lt;/a&gt;. Great idea! I&amp;rsquo;ve been wishing for this for a while.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2026/01/14/its-so-satisfying-seeing-the.html</link>
      <pubDate>Wed, 14 Jan 2026 06:46:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2026/01/14/its-so-satisfying-seeing-the.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s so satisfying seeing the run of a large Go test suite reveal itself as a tree when executed in an editor. IDEs should lean into this, allowing execution of subtests to any depth (not just the top + 1 level), and offering features like find.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: An API For a Keyframe Animation Package</title>
      <link>https://lmika.org/2025/12/30/devlog-an-api-for-a.html</link>
      <pubDate>Tue, 30 Dec 2025 16:42:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/30/devlog-an-api-for-a.html</guid>
      <description>&lt;p&gt;Spent the day building a key-frame animator for an Ebitengine project. All coded in Go, which should make it usable for some other projects in theory. I don&amp;rsquo;t have a lot of experience with animation frameworks — other than as an end-user of apps that consist of a timeline where I place keyframes — so I&amp;rsquo;m not aware of what constitutes best practice for a programatic API. So I set out to build one with an API that made sense to me. After one major iteration, this is the approach I came up with.&lt;/p&gt;
&lt;p&gt;The core model type is the Var, which represents an animatable value. These are essentially handles describing the type of value the (package) user wants to animate. At the moment there is only a single type, which is a float:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewFloatVar&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewFloatVar&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To animate the value, the user will need to create an Animation and place any created Vars on a track. Tracks consist of key-frames, which is essentially a time offset, and one or more values a Var should have at that time. These values can be created from the Var itself, by calling &lt;code&gt;Set()&lt;/code&gt;.   This returns a type holding what that Var should be set to at that key-frame (&amp;ldquo;set&amp;rdquo; is probably not the best term for this):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;moveDown&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewAnimation&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;posTrack&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;moveDown&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewTrack&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;posTrack&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;KeyFrame&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;, []&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatSet&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;posTrack&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;KeyFrame&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3.0&lt;/span&gt;, []&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatSet&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;100.0&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;100.0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Not all Vars need to be included on a particular keyframe, but prior to animating, each of the Vars known to the track will need to be tweened if there isn&amp;rsquo;t a &amp;ldquo;set&amp;rdquo; for them on the key-frame, so it&amp;rsquo;s generally good practice to set all the Vars for each track. Different tracks can have a different of Vars, so if different keyframes are needed, it probably should exist on a separate track.&lt;/p&gt;
&lt;p&gt;Animations are designed to be reusable and simply encode what the Animation is. The motivation here is that all the expensive work should be done once, when the Animation is created. To actually animate the Vars, the user will need to create a timeline and set it to the Animation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewTimeline&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetAnimation&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;moveDown&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will initialise all the Var values to what they would be at frame 0 (at the moment, frame 0 must be a time 0, but down the line I&amp;rsquo;d like to remove this to allow for blending between Animations). The value of each Var can now be retrieved, as Vars only have a value within the scope of a Timeline.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// x and y == 0, as that is the value they were set to in the first frame&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;drawRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Timeline clock is advanced by calling the &lt;code&gt;Advance()&lt;/code&gt; method, which takes a delta in fractions of a second. This will tween all the Vars based on the current action, and the new value can be retrieved by calling &lt;code&gt;Value()&lt;/code&gt; again. Thus the core use of this package is advancing the clock and querying the values for each tweened frame.&lt;/p&gt;
&lt;p&gt;Within an Ebitengine project, this is done by calling &lt;code&gt;Advance&lt;/code&gt; within &lt;code&gt;Update&lt;/code&gt; function and sampling the values within &lt;code&gt;Draw&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Timeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatVar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;animator&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FloatVar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Update&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; float64(&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TPS&lt;/span&gt;())  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Advance&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dt&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Draw&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ebiten&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Image&lt;/span&gt;) {  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;vector&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FillRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;screen&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       float32(&lt;span style=&#34;color:#a6e22e&#34;&gt;xPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)), float32(&lt;span style=&#34;color:#a6e22e&#34;&gt;yPos&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tl&lt;/span&gt;)),  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s an test animation:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.mov/25293/2025/cleanshot-2025-12-30-at-17.14.42/playlist.m3u8&#34; poster=&#34;https://cdn.uploads.micro.blog/25293/2025/frames/1647920-0-36a228.jpg&#34; width=&#34;1232&#34; height=&#34;720&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m quite happy with how this turns out. I like how little &amp;ldquo;magic&amp;rdquo; is involved in the API: values and clocks are not changed from underneath you: you need to explicitly advance the clock yourself, and read the values when you need them.&lt;/p&gt;
&lt;p&gt;Obviously there&amp;rsquo;s room for improvement. Tracks and keyframes cannot be changed; only linear interpolation of float values are supported; and there&amp;rsquo;s no looping, bouncing, or reversing of animations. I&amp;rsquo;d hope to add all this in due time, probably based on the needs of the project I&amp;rsquo;m working on. There are also a few things I&amp;rsquo;m a little wary of, such as a global atomic int responsible for allocating an ID for each newly created Var. And the Timeline holds Var values in a map which I hope is fast enough for rendering on a screen.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s nice to have something like this in my toolbox. It&amp;rsquo;s been something I&amp;rsquo;ve been wishing for a while.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/12/28/achievement-unlocked-setting-up-a.html</link>
      <pubDate>Sun, 28 Dec 2025 10:11:51 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/12/28/achievement-unlocked-setting-up-a.html</guid>
      <description>&lt;p&gt;Achievement unlocked: setting up a CI/CD pipeline the builds a Go WASM project and deploys it to Netlify that worked first try.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Blogging Tools - Image Collections Triage App</title>
      <link>https://lmika.org/2025/11/29/devlog-blogging-tools-image-collections.html</link>
      <pubDate>Sat, 29 Nov 2025 09:54:02 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/29/devlog-blogging-tools-image-collections.html</guid>
      <description>&lt;p&gt;Up until now, I&amp;rsquo;ve been using Blogging Tools&amp;rsquo; in-app notification system to sort images to collections. There were quite a few limitations in doing so: only notifications for the first for images were raised, galleries weren&amp;rsquo;t handled, and you could only select one category per image. I also wanted a way to automatically change the header image in this blog. This was done using the image category notification, but it was pretty basic in that the image was chosen as the header if any category was selected.&lt;/p&gt;
&lt;p&gt;I had ideas of improving all of this but I realised quickly that I needed something more sophisticated to do so. So I added another app to Blogging Tools to do this. I also thought to try out using Antigravity to do this. I was curious to know how well Google Gemini would work with a project that already had an established architecture and coding patterns. I was also curious to know how Antigravity felt when I held a tighter grip of the reigns: I wanted to drive the development work and only turning to the agent when I need something done.&lt;/p&gt;
&lt;p&gt;I started simply enough: I built a basic HTML template for this new app and asked Gemini to prepare a stub handler:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please create a new handler that services the template &amp;ldquo;imageposts/show.html&amp;rdquo;. Implement it much like the other handlers found in the &amp;ldquo;handlers&amp;rdquo; directory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I forgot to mention to the agent that this was to be a stub, and it ended up using the existing provider for the gallery app. But it was pretty close to what I wanted. I then asked it to suggest a good HTML entity for opening a page in an external window.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&amp;rsquo;s a good HTML entity for indicating that a link will open up into an external window/tab?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It suggested &lt;code&gt;↗&lt;/code&gt;, also a good answer. I then went on to build the Go models and wanted to work on the CSS styling next, so I asked Gemini to produce some example data using these models:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please generate 3 stubbed values for this item slice. Use real example image URLs for the source.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It struggled a little on this one, suggesting a URL that didn&amp;rsquo;t exist. But after pointing this out, it got there in the end. It was at this point that I needed to do some refactoring to some of the existing services, needing to extract out a type that used only the values from another type. Gemini did quite well here too:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note the select call. At the moment this is returning a micropub.Item model. I want to make a new model called &amp;ldquo;ImageCollection&amp;rdquo; that uses only the values of this micropub.Item that the code is currently using now. I don&amp;rsquo;t want the micropub.Item changed, but any code that is using this for the purpose of the selected call should include a map to this new model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I guess this is not a novel experience to anyone using Cursor, but what I really liked about this interaction is that I was free to work on other things or move away from the keyboard entirely while the agent was working. This is one annoyance that I have with Claude Code: the exchange pauses and waits for me to approve every change before it proceeds. Having Gemini plan the changes first, then have me approve them once I&amp;rsquo;m back is a much nicer way of working.&lt;/p&gt;
&lt;p&gt;I then turned to the database schema, defining the table schema and SQL queries myself, and asking Gemini to produce the Go code that marshals the data: the so called &amp;ldquo;database providers&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Implement the Go DB provider for DB tables for the queries in &amp;ldquo;imageposts.sql&amp;rdquo;. Do it in a similar style to the other providers found in &amp;ldquo;providers/db&amp;rdquo;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is where I get the most use out of using coding agents. This code is not hard, but it&amp;rsquo;s super annoying to write. I think it also helps that there&amp;rsquo;s an established code base for the agent to work on. I was a little unhappy with what the agent produced last week, probably because it was required to produce it from scratch and in a way that wasn&amp;rsquo;t how I would write it. But here, probably because the agent had existing patterns to work on, it&amp;rsquo;s producing something that looks a lot better, to my eyes at least.&lt;/p&gt;
&lt;p&gt;It did struggle with the providers a little, choosing to merge an update to two models in a single method. I did try to correct it, but I just had to go in and fix it manually.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We can probably separate SaveImagePost into one that saves the ImagePost, and another that saves ImagePostItems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One other thing I discovered is that getting Gemini to write unit tests is important, as it provides a way for the agent to verify it&amp;rsquo;s code. I approved a change Gemini produced without looking too closely, and if I were to test it myself, it would&amp;rsquo;ve failed. But only after I asked it to produce a unit test did I see it fix the errors introduced in the last interaction. Something to think about in the future.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Might be a good idea to implement a unit test to verify that the DB methods in imageposts.go work. Do this by creating a new db.Provider instance with a temporary file, and verifying that the provider methods of imageposts.go work as expected. Please use testify.Assert to do the asserts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It was now time to build the service layer. This is something I wanted to drive myself, occasionally relying on auto-suggestions. Antigravity fell down (ha ha) here a little, producing suggestions that were pretty far from what I wanted. I guess it was lacking the context to know what I actually wanted, which is somewhat understandable as I wasn&amp;rsquo;t giving that context. It also seems to fight with IntelliSence from VS Code sometimes. I occasionally press tab in response to a type suggestion from the index, only to have it produce a completion for some unknown type. I do wish the suggestions use the code index more in general. There&amp;rsquo;ve been more than a few times where the agent suggested types and methods that just didn&amp;rsquo;t exist. Might be something that can be tuned.&lt;/p&gt;
&lt;p&gt;One quality of life improvement I did make was increase the timeout for when suggestions would appear. The default was something like 50 milliseconds, which is pretty short. Bumping it to 1 second helps, but even so they still get in the way. I may bump it a little higher.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100529.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: A web interface for blogging tools features options to categorize or set an image of a scenic landscape as a header.&#34;&gt;
&lt;figcaption&gt;First cut of the collection triage screen at the end of day one.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I worked on this feature over the next couple of evenings, occasionally changing tack which required a change in the database schema. One thing I did like while working on this was getting to the point where I could simply ask the agent to update the provider code whenever I did so, and it knew what I meant:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The ImagePost model and table schema now has a PostDate field/column. Please update the providers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I do get the feeling that the user interaction in Antigravity needs to be refined a little. For someone who is willing to do most of the driving, it would be nice if the editor was a little less &amp;ldquo;needy&amp;rdquo;. This is probably more to do with VS Code being the underlying editor: lots of annoying popups and mouse overs, all shouting for my attention: LOOK AT THIS, LOOK AT THIS. But the AI suggestions don&amp;rsquo;t help. Instead of displaying the generated code, disrupting my sense of where everything is, maybe a more subtler approach is warranted: a small indicator beside the cursor or in the margin indicating that a suggestion is present. I may miss it, but who cares? Remember, I&amp;rsquo;m the one driving here.&lt;/p&gt;
&lt;p&gt;Today, I got the feature finished. The way it works is that it will poll the RSS feed of this blog, and for every post that has an image, it will add an entry for me to triage. Each entry consists of a brief summary, plus the images found within the post:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100017.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: A menu interface displaying various blogging tools and options for managing images, videos, and text.&#34;&gt;
&lt;figcaption&gt;A new &#39;Sort Image Posts&#39; app, with a badge indicating the number of images to triage.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100346.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: Blogging interface featuring a post titled Under the big, grey sky-rail with a small image and options to edit or delete.&#34;&gt;
&lt;figcaption&gt;List of posts with images that need triaging.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Clicking through to the post would list the collections, and I would select the ones I&amp;rsquo;d like the image to be filed in. And this would list all the images of the post, not just the first few. It is all or nothing, unfortunately. There are ways to get the images of the collection, but I couldn&amp;rsquo;t see a way for me to get the collections of an image. I don&amp;rsquo;t think this would be a problem, as Blogging Tools is the only way I actually set the collections of an image.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/pasted-image-20251129100432.png&#34; width=&#34;600&#34; height=&#34;449&#34; alt=&#34;Auto-generated description: A screen displays a blogging tool interface with an image of an elevated roadway and options for categorizing an image post.&#34;&gt;
&lt;figcaption&gt;Selecting a post will list the images found within that post, with the collections they should be added to.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Saving the categories will queue the changes, which will be applied every 5 minutes. A first cut of this feature applied the changes immediately, but the way I imagine using this is that I&amp;rsquo;d go in, triage the images in one go, then leave. Applying all the changes then felt inefficient.&lt;/p&gt;
&lt;p&gt;When it comes to setting the header image, I figured I&amp;rsquo;d add a dedicated collection for that. Every hour a cronjob will query the local database for images that is within this category, and write the URLs to a manifest file in the assets Git repository. A Forgejo Action on that repository runs every night, and will check the date-stamps for the next image to display on the header. If one is found, it will fetch the image, save it as a JPEG with reduced quality and upload to Netlify. Since Netlify flushes the CDN whenever a deployment happens, nothing more needs to be done: no MD5 sum hashes, no adjustment to CSS files. Just update &lt;code&gt;header.jpg&lt;/code&gt; and go:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Example of the manifest file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-22T13:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://lmika.org/uploads/2025/pxl-20251120-083552448.jpg&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;crop&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bottom&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-11-26T13:00:00Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://lmika.org/uploads/2025/a3c74bfebd.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Deployed all these changes to prod and naturally some rework that&amp;rsquo;s needed. First, I completely forgot about galleries. They do come through as image tags in the generated HTML, but I&amp;rsquo;m operating on the source post, and they appear as Hugo short-codes. Fortunately the way I write galleries is pretty standardised now, so I think a regular expression would work here.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;galleryRegexp&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;regexp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MustCompile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`[{]{2}[&amp;lt;][ ]?glightbox.*src=&amp;#34;([^&amp;#34;]+)&amp;#34;.*[&amp;gt;][}]{2}`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, I also want to fix the layout of the collection checkboxes and hide some options, such as those that for past years. And finally, will turn off the existing image category notification logic.&lt;/p&gt;
&lt;p&gt;Deployed again, and now I just need to wait for the next time I post a photo. I suspect some bug fixes or usability adjustments will come from this, but more on that when that happens.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/11/06/kind-of-crazy-seeing-go.html</link>
      <pubDate>Thu, 06 Nov 2025 06:38:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/11/06/kind-of-crazy-seeing-go.html</guid>
      <description>&lt;p&gt;Kind of crazy seeing Go packages which are effectively WASM builds of C libraries running in a Go WASM runtime, like this &lt;a href=&#34;https://github.com/ncruces/go-sqlite3&#34;&gt;sqlite3&lt;/a&gt; port. So that&amp;rsquo;s WASM sandwiched between two layers of Go. A &amp;ldquo;WASM smash burger,&amp;rdquo; if you will. Still, if you want to avoid using Cgo, then I can understand the motivation.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/10/02/interesting-development-in-the-world.html</link>
      <pubDate>Thu, 02 Oct 2025 07:43:55 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/10/02/interesting-development-in-the-world.html</guid>
      <description>&lt;p&gt;Interesting development in the world of Go: in 1.26, the &lt;a href=&#34;https://utcc.utoronto.ca/~cks/space/blog/programming/GoNewWithExpression&#34;&gt;new() function will now accept expressions&lt;/a&gt;, not just types. This returns a pointer to the value, which will be useful for those types that use pointers for their fields:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;Age&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;age&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Age&lt;/span&gt; = new(&lt;span style=&#34;color:#a6e22e&#34;&gt;age&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It also works for literal values, so that temporary &lt;code&gt;age&lt;/code&gt; variable is strictly not necessary, although the linked post does state that it requires some consideration for types. Having &lt;code&gt;user.Age = new(10)&lt;/code&gt; will work without issue as &lt;code&gt;new&lt;/code&gt; will return a &lt;code&gt;*int&lt;/code&gt;; but if &lt;code&gt;Age&lt;/code&gt; were a &lt;code&gt;*uint&lt;/code&gt;, you&amp;rsquo;ll get a type error.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://lmika.org/2024/11/12/ill-never-understand.html&#34;&gt;I go on&lt;/a&gt; about &lt;a href=&#34;https://lmika.org/2025/08/15/dear-go-developers-you-dont.html&#34;&gt;unnecessary pointer types&lt;/a&gt; in the past (and will probably continue to do so in the future). To me, it&amp;rsquo;s just one of those paper-cuts you encounter in your day to day that you know can be made easier. So I consider this a welcome change. It&amp;rsquo;s not going to same me a ton on code, but every little bit helps.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Learning To Like Sentinel Errors In Go</title>
      <link>https://lmika.org/2025/09/18/learning-to-like-sentinel-errors.html</link>
      <pubDate>Thu, 18 Sep 2025 09:24:50 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/18/learning-to-like-sentinel-errors.html</guid>
      <description>&lt;p&gt;I use to be someone who returned a nil value along with a nil error whenever I needed to indicate that a value was missing, like a database record. But recently, I&amp;rsquo;ve come around to the idea of returning either a result, or an error: never returning neither.&lt;/p&gt;
&lt;p&gt;It may not be technically correct. If something doesn&amp;rsquo;t exist in a database, one can make the argument that a function implemented in a way that, when said aloud, would effectively be &amp;ldquo;well, I didn&amp;rsquo;t have any errors trying to find the thing; but I couldn&amp;rsquo;t find the thing either, so I got nothing successfully.&amp;rdquo; And I know that sentinel errors is &lt;a href=&#34;https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully#:~:text=So%2C%20my%20advice%20is%20to%20avoid%20using%20sentinel%20error%20values%20in%20the%20code%20you%20write.%20There%20are%20a%20few%20cases%20where%20they%20are%20used%20in%20the%20standard%20library%2C%20but%20this%20is%20not%20a%20pattern%20that%20you%20should%20emulate.&#34;&gt;generally frowned upon by certain Go language designers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But I think it makes for more readable code; dare I say, more empathetic code. If much of what you write in Go is stuff like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getThingFromDB&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I think it&amp;rsquo;s safe to assume that the reader will think that &lt;code&gt;res&lt;/code&gt; will have a value if &lt;code&gt;err&lt;/code&gt; is nil. I know it&amp;rsquo;s generally not correct to assume that, just like it&amp;rsquo;s not correct to assume that a pointer value is never nil. But us coders are still human (at least for now), and doing the obvious thing is still better than being technically correct.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Devlog: Dequoter — Something Different Today</title>
      <link>https://lmika.org/2025/09/06/devlog-dequoter-something-different-today.html</link>
      <pubDate>Sat, 06 Sep 2025 16:10:04 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/06/devlog-dequoter-something-different-today.html</guid>
      <description>&lt;p&gt;When it comes to hobby projects, I&amp;rsquo;m trying to reduce the number of things I build for work. But I&amp;rsquo;ve decided to revisit one that I&amp;rsquo;ve abandoned, given the frequent need for this. The issue is simple: I have a JSON data structure that is quoted within a string, and I want to &amp;ldquo;unquote&amp;rdquo; it and filter the JSON so that I can read it.&lt;/p&gt;
&lt;p&gt;So I thought I&amp;rsquo;d spend today restarting the Dequoter project. The best way to describe it is &lt;a href=&#34;https://github.com/IvanMathy/Boop&#34;&gt;a Boop clone&lt;/a&gt;. I like Boop, I use Boop. And I know Boop is extendable, so I could implement any processor I need using JavaScript. But I&amp;rsquo;m not a huge fan of JavaScript, and Go has the function I need &lt;a href=&#34;https://pkg.go.dev/strconv@go1.25.1#Unquote&#34;&gt;right there in the standard library&lt;/a&gt;. And with most things I encounter, I want to build my own. I know I shouldn&amp;rsquo;t, and I&amp;rsquo;ve been better at resisting. But I&amp;rsquo;d figured this could be a nice little distraction. And I thought I could get much of what I need done in a morning.&lt;/p&gt;
&lt;p&gt;So I started a new &lt;a href=&#34;https://wails.io&#34;&gt;Wales project&lt;/a&gt; and after about 3 hours, I had a scratchpad with an invokable command palette that I can use to dequote a string and format a JSON object:&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-09-06-at-15.57.26.mp4&#34; poster=&#34;https://lmika.org/uploads/2025/ecf16f805a.png&#34; controls=&#34;controls&#34; preload=&#34;metadata&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Granted, it&amp;rsquo;s not much for 3 hours. While I did use Claude Code a little, much of this was hand rolled. And I spent a lot of time learning the ins and outs of &lt;a href=&#34;https://codemirror.net&#34;&gt;Code Mirror&lt;/a&gt;. This is why I wanted to use Wales this time around. The &lt;a href=&#34;https://lmika.org/2024/07/20/continuing-my-exploration.html&#34;&gt;previous run at this&lt;/a&gt; used &lt;a href=&#34;https://fyne.io/&#34;&gt;Fyne&lt;/a&gt;, which was really nice to use, but the available widgets were quite limited. The nice thing about Wales is that I can build the UI in HTML and it uses the built-in HTML renderer from the OS: no need to bundle Chrome. The frontend is mainly vanilla HTML and JavaScript, although I am using &lt;a href=&#34;https://stimulus.hotwired.dev&#34;&gt;Stimulus&lt;/a&gt; to help with some of the controls. The backend is, of course, Go.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/cleanshot-2025-09-06-at-15.46.43.png&#34; width=&#34;400&#34; height=&#34;572&#34; alt=&#34;Auto-generated description: Window displaying file information for an application named dequoter, including details like size, location, and version.&#34;&gt;
&lt;figcaption&gt;Final build size is 8.4 MB.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;By the end of this morning&amp;rsquo;s session, what I had was not pretty, but it&amp;rsquo;s functional and it already does what I need it to do. I do have some plans for integrating this with UCL, and using that to define processors. That should hopefully leave all the heavy core stuff to be built near the beginning, and that could sit pretty while the extensions are added in a nicer language. Anyway, more on this if this goes anywhere.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/09/04/github-gopher-hawaiian-shirts-patterns.html</link>
      <pubDate>Thu, 04 Sep 2025 08:56:27 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/09/04/github-gopher-hawaiian-shirts-patterns.html</guid>
      <description>&lt;p&gt;🔗 &lt;a href=&#34;https://github.com/rsc/gophershirt&#34;&gt;GitHub: Gopher Hawaiian Shirts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Patterns for printing Hawaiian shirts with the Go gopher. I think I&amp;rsquo;ve found what I&amp;rsquo;ll be wearing to work in the future. 😄&lt;/p&gt;
&lt;p&gt;Via: &lt;a href=&#34;https://golangweekly.com/issues/568&#34;&gt;Golang Weekly&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/08/15/dear-go-developers-you-dont.html</link>
      <pubDate>Fri, 15 Aug 2025 06:26:35 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/15/dear-go-developers-you-dont.html</guid>
      <description>&lt;p&gt;Dear Go developers,&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t need to return pointers,&lt;br&gt;
Unless you do need to return pointers.&lt;br&gt;
But if you think you need to return pointers,&lt;br&gt;
You probably don&amp;rsquo;t need to return pointers.&lt;/p&gt;
&lt;p&gt;Instead, consider just returning regular struct values. Keep the nil-pointer panics at bay.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/08/07/the-exhaustive-go-linter-complaining.html</link>
      <pubDate>Thu, 07 Aug 2025 16:08:25 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/07/the-exhaustive-go-linter-complaining.html</guid>
      <description>&lt;p&gt;The &lt;code&gt;exhaustive&lt;/code&gt; Go linter complaining about missing cases for switch statements with a &lt;code&gt;default&lt;/code&gt; clause is killing me.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;missing cases in switch of type this, and this, and this, and this, and…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2025/out-20250807-160348.jpg&#34; width=&#34;600&#34; height=&#34;328&#34; alt=&#34;Auto-generated description: A man in an office setting sits on a couch next to a bottle, with the text That&#39;s what the default is for.&#34;&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/08/05/request-for-a-go-linter.html</link>
      <pubDate>Tue, 05 Aug 2025 11:24:24 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/08/05/request-for-a-go-linter.html</guid>
      <description>&lt;p&gt;Request for a go linter: something that would warn when an variable with the name &lt;code&gt;err&lt;/code&gt; is not of type &lt;code&gt;error&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Bla&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// &amp;#39;err&amp;#39; not of type &amp;#39;error&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Would&amp;rsquo;ve saved me a few hours today trying to test if a Future was not-nil, without actually waiting for the result.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Moan-routine: LO&#39;s Predicate Signatures</title>
      <link>https://lmika.org/2025/07/11/moanroutine-los-predicate-signatures.html</link>
      <pubDate>Fri, 11 Jul 2025 17:05:45 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/07/11/moanroutine-los-predicate-signatures.html</guid>
      <description>&lt;p&gt;I wish I liked using &lt;a href=&#34;https://github.com/samber/lo&#34;&gt;lo&lt;/a&gt;. People at work swear by it but whenever I use it, I just get annoyed at how much better it could be.&lt;/p&gt;
&lt;p&gt;For those unaware of what &lt;code&gt;lo&lt;/code&gt; is, it&amp;rsquo;s a Go package inspired by &lt;a href=&#34;https://lodash.com&#34;&gt;lodash&lt;/a&gt;, the utility library for JavaScript that allows one to use higher-order functions for operating over collections. Think mapping, filtering, etc. Given that &lt;code&gt;lodash&lt;/code&gt; is built for JavaScript, it&amp;rsquo;s allows for a lot of flexibility in the closures passed to the various functions. &lt;code&gt;map&lt;/code&gt;, for instance, allows a closure that takes two arguments which, when used with an array, will be set to each item&amp;rsquo;s value and index. If you need both bits of information, define a closure with two arguments. If you just need the value, just use one.&lt;/p&gt;
&lt;p&gt;Given Go&amp;rsquo;s strong type system, this flexibility is not granted to us Go developers. Whatever the closure signature is defined for &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#Map&#34;&gt;Map&lt;/a&gt; or &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#Map&#34;&gt;Filter&lt;/a&gt; is the one you&amp;rsquo;re stuck with (well, that&amp;rsquo;s not entirely true, but more on that in a bit). So it&amp;rsquo;s important for the library developer to come up with some good defaults.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure the developer of &lt;code&gt;lo&lt;/code&gt; was trying to be helpful by defining &lt;code&gt;Map&lt;/code&gt; and &lt;code&gt;Filter&lt;/code&gt;  to accept a closure that takes the item&amp;rsquo;s  value and index. But by doing so, they totally screwed me over for the simple reason in that they&amp;rsquo;re not consistent. I can&amp;rsquo;t pass the same function into both &lt;code&gt;Filter&lt;/code&gt; and &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#EveryBy&#34;&gt;EveryBy&lt;/a&gt; , as the latter does not accept a closure with an index.&lt;/p&gt;
&lt;p&gt;This prevents me from predefining a set of predicates that I can reused. Being able to define a closure in Go is nice, but you know what&amp;rsquo;s better? Not having to define a closure. Riddle me this: which of these is nicer to read? This:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingsWithShapes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;areAllRects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EveryBy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or this?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingsWithShapes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;areAllRects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EveryBy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Moot point, as the first one is impossible.&lt;/p&gt;
&lt;p&gt;The mistake of &lt;code&gt;Map&lt;/code&gt; providing two arguments is subtler. When I&amp;rsquo;m operating on a database, I&amp;rsquo;m usually find myself having to map from the types generated from &lt;a href=&#34;https://sqlc.dev&#34;&gt;sqlc&lt;/a&gt; into my own models. Moving this logic out into a separate function makes sense, one that I can invoke when I&amp;rsquo;m dealing with a result set — in which case I could pass this mapping function to &lt;code&gt;Map&lt;/code&gt; — or a single row — in which case I just call function directly. But requiring the &lt;code&gt;Map&lt;/code&gt; closure to take the index screws me here too. I could defining a mapping function that accepts an index that I won&amp;rsquo;t use, but that just makes the direct calls look rubbish. Am I just going to pass in 0 here? Urgh!&lt;/p&gt;
&lt;p&gt;Now, I alluded to the function signatures potentially being a little more flexible. One could use the method of &lt;a href=&#34;https://en.wikipedia.org/wiki/Currying&#34;&gt;currying&lt;/a&gt; which will return a function that will take the first argument and ignore the second. This could allow the same predicate to be used in both &lt;code&gt;Filter&lt;/code&gt; and &lt;code&gt;EveryBy&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ignoreSecondArg&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;U&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;V&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fn&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shape&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;corners&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;doThingsWithShapes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt; []&lt;span style=&#34;color:#a6e22e&#34;&gt;Shape&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ignoreSecondArg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;areAllRects&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lo&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;EveryBy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;shapes&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isRect&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It would&amp;rsquo;ve been nice for &lt;code&gt;lo&lt;/code&gt; to provided these functions. The closest I see are the &lt;a href=&#34;https://pkg.go.dev/github.com/samber/lo#Partial&#34;&gt;Partial&lt;/a&gt; functions which &lt;em&gt;almost&lt;/em&gt; do what I need. Yet they&amp;rsquo;re designed for pegging the first argument to a value and returning a function that takes the second one. I&amp;rsquo;ve have no need for these sorts of function, and having something which could help in adapting these predicates would seem much more useful to me.&lt;/p&gt;
&lt;p&gt;(Deep breath)&lt;/p&gt;
&lt;p&gt;Okay, I can see how this all looks a little unfair. And I&amp;rsquo;m not going to say the developer or users are making mistakes here. Well, I am, but I&amp;rsquo;m not say that they&amp;rsquo;re incompetent or have malicious intent. I&amp;rsquo;m sure there are good reasons behind these decisions.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s just… this could be so much better than it currently is. Consider this criticism from someone who&amp;rsquo;d like to see this package succeed, like a parent or a coach trying to help a struggling child. We&amp;rsquo;re not using JavaScript here; we&amp;rsquo;re bound to the type system we have. Embrace that. Recognise that the occasional closure is fine but can get unwieldily whenever we&amp;rsquo;re forced to make one with one of your functions. The flexibility you provide here is an example of the perfect being the enemy of the good.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2025/05/13/does-google-ever-regret-naming.html</link>
      <pubDate>Tue, 13 May 2025 08:18:40 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2025/05/13/does-google-ever-regret-naming.html</guid>
      <description>&lt;p&gt;Does Google ever regret naming Go &amp;ldquo;Go&amp;rdquo;? Such a common word to use as a proper noun. I know the language devs prefer not to use Golang, but there&amp;rsquo;s no denying that it&amp;rsquo;s easier to search for.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Broadtail</title>
      <link>https://lmika.org/2024/01/24/broadtail.html</link>
      <pubDate>Wed, 24 Jan 2024 02:10:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2024/01/24/broadtail.html</guid>
      <description>&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; 2021 – 2022&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Paused&lt;/p&gt;
&lt;p&gt;First project I&amp;rsquo;ll talk about is Broadtail. I think I talked about this
one before, or at least I posted screenshot of it. I started work on
this in 2021. The pandemic was still raging, and much of my downtime was
watching YouTube videos. We were coming up to a federal election, and I
was getting frustrated with seeing YouTube ads from political parties
that offend me. This was before YouTube Premium so there was no real way
to avoid these ads. Or was there?&lt;/p&gt;
&lt;h3 id=&#34;a-frontend-for-youtube-dl&#34;&gt;A Frontend For youtube-dl&lt;/h3&gt;
&lt;p&gt;I had some experience with youtube-dl in the past, downloading and
saving videos that I hoped to watch later. I recently discovered that
YouTube also published RSS feeds for channels and playlist. So I was
wondering if it was possible to build something that could use both of
these. My goal was to have something that would allow me to subscribe to
YouTube channels via RSS, download videos using youtube-dl, and watch
them via Plex on my TV. This was to be deployed on a Intel Nuc that I
was using as a home server, and be accessible via the web-browser.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/db017f8413.jpg&#34;&gt;
&lt;figcaption&gt;An early version of Broadtail, with only the download frontend.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I deceided to get the YouTuble downloading feature built first. I
started a new Go project and got something up and running reasonably
quickly. It was a good excuse to get back to vanilla Go web development,
using http.Handle and Go templates, instead of relying on frameworks
like Buffalo (don&amp;rsquo;t get me wrong, I still like Buffalo, but it is quite
heavy handed).&lt;/p&gt;
&lt;p&gt;It was also an excuse to try out StormDB, which is an embedded NoSQL
data store. The technology behind it is quite good — it&amp;rsquo;s used B-Trees
memory mapped files under the cover — and I tend to use it for other
things as well. It proved to be quite usable, apart from not allowing
multiple read/writers at the same time, which made deployments
difficult.&lt;/p&gt;
&lt;p&gt;But the backend code was the easy part. What I lack was any sense of web
design. That&amp;rsquo;s one good thing about a framework like Buffalo: it comes
with a usable style framework out of the box (Bootstrap). If I were to
go my own way, I&amp;rsquo;d have to start from scratch.&lt;/p&gt;
&lt;p&gt;The other side of that coin though, is that it would give me the freedom
to go for something that&amp;rsquo;s slightly off-beat. So I went for an
aesthetics that reminded me of early 2000 web-design: san-serif font,
all grey lines, dull pastels colours, small controls and widgets (I
stopped short at gradients and table-based layouts).&lt;/p&gt;
&lt;p&gt;This version also included a hand-rolled job manager that I used for a
bunch of other things. It&amp;rsquo;s… fine. I wouldn&amp;rsquo;t use it for anything
&amp;ldquo;real&amp;rdquo;, but it had a way of managing job lifecycles, updating
progress, and the ability to cancel a running job. So for that, it was
good enough.&lt;/p&gt;
&lt;p&gt;Finally, it needed a name. At the time, I was giving all projects
bird-like codename, since I can&amp;rsquo;t come up with names that I liked. I
eventually settled on Broadtail, which was a reference to &lt;a href=&#34;https://en.wikipedia.org/wiki/Broad-tailed_parrot&#34;&gt;broadtail
parrots&lt;/a&gt;, like the rosella.&lt;/p&gt;
&lt;h3 id=&#34;rss-subscriptions&#34;&gt;RSS Subscriptions&lt;/h3&gt;
&lt;p&gt;It didn&amp;rsquo;t take long after I got this up and running before I realised I
needed the RSS subscription feature. So that was the next thing I
added.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/02c7ec1b20.jpg&#34;&gt;
&lt;figcaption&gt;Homepage of the most recent version of Broadtail, with an active
download and a couple of favourited items.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;That way it worked was pretty straight forward. One would setup a
subscription to a YouTube channel or playlist. Broadtail will then poll
that RSS subscription every 15 minutes or so, and show new videos on the
homepage. Clicking that video item would bring up details and an option
to download it.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/7811391b7b.jpg&#34;&gt;
&lt;figcaption&gt;Video details. This one is simulated as I couldn&#39;t get youtube-dl working.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Each RSS subscription had an associated target directory. Downloading an
ad-hoc video would just dump it in a configured directory but I wanted
to make it possible to organise downloads from feeds in a more
structured way. This wasn&amp;rsquo;t perfect though: I can&amp;rsquo;t remember the
reason but I had some trouble with this, and most videos just ended up
in the download directory by default (it may have had to do with making
directories or video permissions).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/7b51bf5401.jpg&#34;&gt;
&lt;figcaption&gt;Click a feed to view the most recent items.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Only the feed polling was automatic at this stage. I was not interested
in having all shows downloaded, as that would eat up on bandwidth and
disk storage. So users still had to choose which videos they wanted to
download. The list of recent feed items were available from the
home-screen so they were able to just do so from there.&lt;/p&gt;
&lt;p&gt;I also wanted to keep abreast with what jobs were currently running, so
the home-screen also had the list of running job.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/6869f68642.jpg&#34;&gt;
&lt;figcaption&gt;Click the job to view it&#39;s details. This one failed.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The progress-bar was powered by a web-socket backed by a goroutine on
the server side, which meant realtime updates. Clicking the job would
also show you the live output of the youtube-dl command, making it easy
to troubleshoot any jobs that failed. Jobs could be cancelled at any
time, but one annoying thing that was missing was the ability to retry
failed job. If a download failed, you had to spin up a new job from
scratch. This meant clearing out the old job from the file-system and
finding the video ID again from wherever you found it.&lt;/p&gt;
&lt;p&gt;If you were interested in a video but were not quite ready to download
it right away, you could &amp;ldquo;favourite&amp;rdquo; it by clicking the star. This was
available in every list that showed a video, and was a nightmare to code
up, since I was keeping references to where the video came from, such as
a feed or a quick look. Keeping atop of all the possible references were
become difficult with the non-relational StormDB, and the code that
handled this became quite dodgy (the biggest issue was dealing with
favourites from feeds that were deleted).&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/c975ebbac0.jpg&#34;&gt;
&lt;figcaption&gt;Clicking &#34;Favourites&#34; would show the items that you starred.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&#34;rules--wwdc-videos&#34;&gt;Rules &amp;amp; WWDC Videos&lt;/h3&gt;
&lt;p&gt;The basics were working out quite well, but it was all so manual. Plus,
going from video publication to having something to watch was not
timely. The RSS feed from YouTube was always several hours out of date,
and downloading whole videos took quite a while (it may not have been
realtime, but it was pretty close).&lt;/p&gt;
&lt;p&gt;So one of the later things I added was a feature I called &amp;ldquo;Rules&amp;rdquo;.
These were automations that would run when the RSS feed was polled, and
would automatically download videos that met certain criteria (you could
also hide them or mark them as downloaded). I quite enjoy building these
sorts of complex features, where the user is able to do configure
sophisticated automatic tasks, so this was a fun thing to code up. And
it worked: video downloads would start when they become available and
would usually be in Plex when I want to watch it (it was also possible
to ping Plex to update the library once the download was finished). It
wasn&amp;rsquo;t perfect though: not retrying failed downloads did plague it a
little. But it was good enough.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/25293/2024/619c14dab2.jpg&#34;&gt;
&lt;figcaption&gt;Editing a Rule.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This was near the end of my use of Broadtail. Soon after adding Rules, I
got onto the YouTube Premium bandwagon, which hid the ads and removed
the need for Broadtail as a whole. It was a good thing too, as the Plex
Android app had this annoying habit of causing the Chrome Cast to hang,
and the only way to recover from this was to reboot the device.&lt;/p&gt;
&lt;p&gt;So I eventually returned to just using YouTube, and Broadtail was
eventually abandoned.&lt;/p&gt;
&lt;p&gt;Although, not completely. One last thing I did was extend Broadtail&amp;rsquo;s
video download capabilities to include Apple WWDC Videos. This was
treated as a special kind of &amp;ldquo;feed&amp;rdquo; which, when polled, would scrap
the WWDC video website. I was a little uncomfortable doing this, and I
knew when videos were published, they wouldn&amp;rsquo;t change. So this &amp;ldquo;feed&amp;rdquo;
was never polled and the user had to refresh it automatically.&lt;/p&gt;
&lt;p&gt;Without the means to stream them using AirPlay, downloading them and
making them available in Plex was the only way I knew of watching them
on my TV, which is how I prefer to watch them.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s what Broadtail is primarily used for now. It&amp;rsquo;s no longer
running as a daemon: I just boot it up when I want to download new
videos. And although it&amp;rsquo;s only a few years old, it&amp;rsquo;s starting to show
signs of decay, with the biggest issue being youtube-dl slowly being
abandoned.&lt;/p&gt;
&lt;p&gt;So it&amp;rsquo;s unlikely that I&amp;rsquo;ll put any serious efforts into this now. But
if I did, there are a few things I&amp;rsquo;d like to see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication added with username/password&lt;/li&gt;
&lt;li&gt;Retry failed video downloads.&lt;/li&gt;
&lt;li&gt;The ability to download YouTube videos in audio only (all these
&amp;ldquo;podcasts&amp;rdquo; that are only available as YouTube videos… 😒)&lt;/li&gt;
&lt;li&gt;The ability to handle the lifecycle of videos a little better than it
does now. It&amp;rsquo;s already doing this for errors: when a download fails,
the video is deleted. But it would be nice if it did things like
automatically delete videos 30 days after downloading them. This would
require more control over the &amp;ldquo;video store&amp;rdquo; though.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, that&amp;rsquo;s Broadtail.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://lmika.org/2021/06/26/why-i-like.html</link>
      <pubDate>Sat, 26 Jun 2021 08:26:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2021/06/26/why-i-like.html</guid>
      <description>&lt;p&gt;Why I like developing in Go vs. something like NodeJS: my development setup doesn&amp;rsquo;t randomly break for some weird reason.  When it does break, it&amp;rsquo;s because of something I did explicitly.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why I&#39;m Considering Building A Blogging CMS</title>
      <link>https://lmika.org/2020/11/19/on-building-vs.html</link>
      <pubDate>Thu, 19 Nov 2020 08:29:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/11/19/on-building-vs.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m planning to start a new blog about Go development and one of the things that I&amp;rsquo;m currently torn on is how to host it.  The choice look to be either using a service like &lt;a href=&#34;https://blot.im/&#34;&gt;blot.im&lt;/a&gt; or &lt;a href=&#34;https://micro.blog/&#34;&gt;micro.blog&lt;/a&gt; or some other hosting service, using a static site generation tool like &lt;a href=&#34;https://gohugo.io&#34;&gt;Hugo&lt;/a&gt;, or building my own CMS for it.  I know that one of the things people tell you about blogging is that building your CMS is not worth your time: I myself even described it as &amp;ldquo;second cardinal sin of programming&amp;rdquo; on my &lt;a href=&#34;https://lmika.micro.blog/2020/07/12/ive-signed-up.html&#34;&gt;first post to micro.blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nevertheless, I think at this stage I will actually do just that.  Despite the effort that comes from building a CMS I see some advantages in doing so:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The (theoretical) ability to blog from anywhere:&lt;/strong&gt;  This is one of the weaknesses of static site hosting that I&amp;rsquo;ve run into when trying this approach before.  I tend to work on different machines throughout the week, which generally means that when I find inspiration, I don&amp;rsquo;t have the source files on hand to work on them.  The source files are kept in source control and are hosted on GitHub, but I&amp;rsquo;ve find that I tend to be quite lax with making sure I have the correct version checked out and that any committed changes are pushed.  This is one of the reasons why I like micro.blog: having a service with a web interface that I can just log into, and that will have all the posts there, means that I can work on them as long as I have an internet connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full control over the appearance and workflow:&lt;/strong&gt; Many of the other services provide the means for adjusting the appearance of the web-page, so this is only a minor reason for taking on this effort.  But one thing that I would find useful is to have some control over is the blogging workflow itself.  There are some ideas that I might like to include, like displaying summaries in the main page, or sharing review links for posts prior to publishing them.  Being able to easily do that in a codebase that I&amp;rsquo;m familiar with would help.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Good practice of my skills:&lt;/strong&gt; As someone who tends to work on backend systems for his day-to-day job, some of my development and operational experience are a little rusty.  Building, hosting and operating a site would provide an opportunity to exercise these muscles, and may also come in handy if I were to choose to build something for others to use (something that I&amp;rsquo;ve been contemplating for a while).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that price is not one of these reasons.  In fact it might actually cost me a little more to put together a site like this.  But I think the experience and control that I hope to get out of this endeavour might be worth it.&lt;/p&gt;
&lt;p&gt;I am also aware of some of the risks of this approach.  Here is how I plan to mitigate them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Security and stability:&lt;/strong&gt; This is something that comes for free from a blogging platform that I&amp;rsquo;ll need to take on myself.  There&amp;rsquo;s always a risk with putting a new system onto the internet, and having a web site with remote administration is an invitation for others to abuse it.  To me this is another area of development I believe I need to work on.  Although I don&amp;rsquo;t intend to store any personal information but my own, I do have to be mindful of the risks of putting anything online, and making sure that the appropriate mitigations are in place to prevent that.  I&amp;rsquo;ll also have to make sure that I&amp;rsquo;m maintaining proper backups of the content, and periodically exercising them to make sure they work.  The fact that my work is at stake is a good incentive to keep on top of this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distractions:&lt;/strong&gt; Yeah, this is a classic problem with me: I use something that I build, I find a small problem or something that can be improved, then instead of actually finishing the task, I actually work on the code for the service.  This may have to be something that only gets addressed with discipline.  It may help using the CMS on a machine that doesn&amp;rsquo;t have the source code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I will also have to be aware of the amount of time I put into this.  I actually started working on a CMS several months ago so I&amp;rsquo;m not starting completely from scratch, but I&amp;rsquo;ve learnt with too many other of my personal projects that maintaining something like this is for the long term.  It might be fine to occasionally tinker with it, but I cannot spend too much effort working on the system at the expense of actually writing content.&lt;/p&gt;
&lt;p&gt;So this is what I might do.  I might give myself the rest of the month to do what I need to do to get it up to scratch, then I will start measuring how much time I spend working on it, vs. the amount of time I actually use it to write content.  If the time I spend working on the code base is more than 50% of time I use it to write content, then it will indicate to me that it&amp;rsquo;s a distraction and I will abandon it for an alternative setup.  To keep myself honest, I&amp;rsquo;ll post the outcomes of this on my micro blog (if I remember).&lt;/p&gt;
&lt;p&gt;A few other minor points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Will this delay publishing of the blog?  No. The CMS is functionally complete but there are some rough edges that I&amp;rsquo;d like to smooth out.  I hope to actually start publishing this new blog very shortly.&lt;/li&gt;
&lt;li&gt;Will I be moving the hosting of this blog onto the new CMS?  No, far from it. The services here works great for how I want to maintain this blog, and the community aspects are fantastic.  The CMS also lacks the IndiWeb features that micro.blog offers and it may be some time before they get built.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ll be interested if anyone has any thoughts on this so feel free to reply to this post on micro.blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I&amp;rsquo;ve posted a &lt;a href=&#34;https://lmika.org/2020/12/13/revising-the-decision.html&#34;&gt;follow-up&lt;/a&gt; on this decision about a month after writing this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Offical support for file embedding coming to Go</title>
      <link>https://lmika.org/2020/10/31/offical-support-for.html</link>
      <pubDate>Sat, 31 Oct 2020 07:50:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/10/31/offical-support-for.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m excited to see, via &lt;a href=&#34;https://golangweekly.com/issues/336&#34;&gt;Golang Weekly&lt;/a&gt;, that the &lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/draft-embed.md&#34;&gt;official support for embedding static files in Go&lt;/a&gt; is being realised, with the &lt;a href=&#34;https://github.com/golang/go/commit/25d28ec55aded46e0be9c2298f24287d296a9e47&#34;&gt;final commit merged to the core a couple of days ago&lt;/a&gt;.  This, along with the &lt;a href=&#34;https://go.googlesource.com/proposal/+/master/design/draft-iofs.md&#34;&gt;new file-system abstraction&lt;/a&gt;, will mean that it will be much easier to embed files in Go applications, and make use of them within the application itself.&lt;/p&gt;
&lt;p&gt;One of the features I like about coding is Go is that the build artefacts are statically linked executables that can be distributed without any additional dependencies.  This means that if I wanted to share something, all I need to do is to give them a single file that they can just run, without needing to worry about whether particular dependencies or runtimes are installed prior to doing so.&lt;/p&gt;
&lt;p&gt;However there are times when the application requires static files, and to maintain this simple form of distribution, I generally want to embed these files within the application itself.  This comes up surprisingly often and is not something that was officially supported within the core language tools, meaning that this gap had to be filled by 3rd parties.  Given the &lt;a href=&#34;https://github.com/golang/go/wiki/Projects#resource-embedding&#34;&gt;number of tools available to do this&lt;/a&gt;, I can see that I&amp;rsquo;m not alone in needing this.  And as great as it is to see the community step in to fill this gap, relying on an external tool complicates the build process a bit&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;: making sure the tool is installed, making sure that it is executed when the build run, making sure that the tool is actively being maintained so that changes to the language will be supported going forward, etc.&lt;/p&gt;
&lt;p&gt;One other thing about these tools is that the API used to access the file is always slightly different as well, meaning that if there&amp;rsquo;s a need to change tools, you end up needing to change your code to actually access the statically embedded file.&lt;/p&gt;
&lt;p&gt;Now that embedding files is officially coming to the language itself, there is less need to rely on all of this.  There&amp;rsquo;s no need to worry about various tools being installed on the machines that are building the application.  And the fact that this feature will work hand in hand with the new file system abstraction means that embedded files would be easier to work with within the code base itself.&lt;/p&gt;
&lt;p&gt;So kudos to the core Go development team.  I&amp;rsquo;m really looking forward to using this.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Although not as much as many other languages.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Remarks on Go&#39;s Error Handling using Facebook&#39;s SDK Crashes As a Framing Device</title>
      <link>https://lmika.org/2020/07/14/there-are-new.html</link>
      <pubDate>Tue, 14 Jul 2020 08:43:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/14/there-are-new.html</guid>
      <description>&lt;p&gt;There are new &lt;a href=&#34;https://mjtsai.com/blog/2020/07/13/another-facebook-sdk-crash/&#34;&gt;reports of Facebook&amp;rsquo;s SDK crashing apps again&lt;/a&gt; due to server changes.  The post above links to Bugsnag article which explores the underlying cause: that&amp;rsquo;s worth a read.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to throw a shout-out to Go&amp;rsquo;s approach to error handling here.  I&amp;rsquo;m not saying that this shows the superiority of Go over Objective C: these sorts of things can happen in any language.  The difference I want to highlight is that Go treats error handling as part of the standard flow of the language, rather than the exceptional flow.  This forces you to think about error conditions when you&amp;rsquo;re making calls to code that can fail.&lt;/p&gt;
&lt;p&gt;This does result in some annoying code of the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;result, err := doOperation()
if err != nil {
    return nil, err
}

result2, err := doSecondOperation(result)
if err != nil {
    return nil, err
}

// and so on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and there&amp;rsquo;s nothing stopping your from completely ignoring the error.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s no way to call these two functions without dealing with the error in some way.  That is, there&amp;rsquo;s no way to simply write &lt;code&gt;doSecondOperation(doOperation())&lt;/code&gt;: you&amp;rsquo;re given an error and you have to do something with it.  So you might as well handle it gracefully.&lt;/p&gt;
&lt;p&gt;P.S. I should probably state that I know very little about Objective C.  I do know that a fair number of APIs in AppKit and UIKit make use of completion handlers which can provide an error value, although to me it seems a littler easier to ignore it vs. deailing with the error values in Go.  I also know that Swift makes improvements here, forcing you to prefix calls that can fail with the &lt;code&gt;try&lt;/code&gt; keyword.  Again, this is not to rag on Objective C; rather it&amp;rsquo;s a comment on the idioms of error handling in Go and how these sort of events could prevent the app from crashing.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Go’s Type Parameters Proposal</title>
      <link>https://lmika.org/2020/07/12/on-gos-type.html</link>
      <pubDate>Thu, 25 Jun 2020 11:30:00 +1000</pubDate>
      
      <guid>http://lmika.micro.blog/2020/07/12/on-gos-type.html</guid>
      <description>&lt;p&gt;The developers of Go have release a new draft proposal for type parameters.  The biggest change is the replacing the concept of constraints, which complicated the proposal somewhat, and replaced it with interfaces to express the same thing.  You can read the proposal here &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md&#34;&gt;latest proposal here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think they’re starting to reach a great balance between what currently exists in the language, and the features required to make a useful type parameter system.  The use of interfaces to constrain the type, that is declare the operators that a type must implement in order to be used as a type parameter for a function or struct, makes total sense.  It also makes moving to type parameters in some areas of the standard library trivial.  For example, the &lt;a href=&#34;https://golang.org/pkg/sort/#Sort&#34;&gt;sort.Sort&lt;/a&gt; function prototype:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Sort(data Interface)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;can simply be written as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Sort(type T Interface)(data T)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I do have some minor concerns though.  The biggest one is the use of interfaces to express constraints related to operators, which are expressed as &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#type-lists-in-constraints&#34;&gt;type lists&lt;/a&gt;.  I think listing the types that a particular type parameter can instantiate makes sense.  It dramatically simplifies the process of expressing a constraint based on the operators a particular type supports.  However, using the concept of interfaces for this purpose seems a little strange, especially so when these interfaces cannot be used in areas &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#type-lists-in-interface-types&#34;&gt;other than type constraints&lt;/a&gt;.  To be fair, it seems like they recognise this, and I&amp;rsquo;m suspecting that in practice these interfaces will be defined in a package that can simply be used, thereby not requiring us to deal with them directly unless we need to.&lt;/p&gt;
&lt;p&gt;But all in all, this looks promising.  It is starting to feel like the design is coming together, with the rough edges starting to be smoothed out.  I appreciate the level of careful consideration the core Go developers are exhibiting in this process.  This is after all a large change to the language, and they only have one real chance at this.  Sure we have to wait for it, but especially in language design, mistakes are forever.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>