diff --git a/rss.xml b/rss.xml index 04ac1e9..22fdc2c 100644 --- a/rss.xml +++ b/rss.xml @@ -6,7 +6,7 @@ An unordered list of things I miss in Go https://github.com/thiagokokada/blog/blob/main/2024-08-17/01-an-unordered-list-of-things-i-miss-in-go.md - <p>I like Go. I think it is a <a href="https://github.com/thiagokokada/blog/blob/main/2024-07-29/02-go-a-reasonable-good-language.md">reasonable good language</a>, and has some good qualities that makes up for its flaws. However, this doesn't mean I think the language couldn't be better, far from it.</p> <p>This blog post is a list of things that I miss from Go from other languages. Some of the things here could probably be implemented soon, some other would probably need a major revision of the language. The list is unordered, because this makes it easier for me to update in the future if I found something else, but also because I don't want to think too hard about giving each point here a rank.</p> <p>With all above, let's start.</p> <h2>Ordered maps in standard library</h2> <p>When I first learned about <a href="https://docs.python.org/3/library/stdtypes.html#typesmapping">dictionaries</a> in Python it quickly became one of my favorite data structures ever. They're extremely versatile, and most modern programming languages have something similar in its standard library. Go isn't different, it has <a href="https://go.dev/blog/maps"><code>map</code></a>, that is Go implementation of a <a href="https://en.wikipedia.org/wiki/Hash_table">hash table</a>. However <code>map</code> in Go are quirky, for example:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() { </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">bool</span>{<span style="color:#e6db74">&#34;foo&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;bar&#34;</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">&#34;baz&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;qux&#34;</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">&#34;quux&#34;</span>: <span style="color:#66d9ef">true</span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">k</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">m</span> { </span></span><span style="display:flex;"><span> println(<span style="color:#a6e22e">k</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span>$ go run ./test.go </span></span><span style="display:flex;"><span>bar </span></span><span style="display:flex;"><span>baz </span></span><span style="display:flex;"><span>qux </span></span><span style="display:flex;"><span>quux </span></span><span style="display:flex;"><span>foo </span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"> </span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>$ go run ./test.go </span></span><span style="display:flex;"><span>foo </span></span><span style="display:flex;"><span>bar </span></span><span style="display:flex;"><span>baz </span></span><span style="display:flex;"><span>qux </span></span><span style="display:flex;"><span>quux </span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"> </span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>$ go run ./test.go </span></span><span style="display:flex;"><span>qux </span></span><span style="display:flex;"><span>quux </span></span><span style="display:flex;"><span>foo </span></span><span style="display:flex;"><span>bar </span></span><span style="display:flex;"><span>baz </span></span></code></pre><p>Now, I don't expect any hash table implementation to keep the order of the elements, but Go actually <a href="https://victoriametrics.com/blog/go-map/">randomise each map instance</a>:</p> <blockquote> <p>But here’s the deal, while the hash function used for maps in Go is consistent across all maps with <strong>the same key type</strong>, the <code>seed</code> used by that hash function is different for each map instance. So, when you create a new map, Go generates a random seed just for that map.</p> </blockquote> <p>While I understand the reason for this (i.e.: to avoid developers relying in a specific iteration order), I still find it weird, and I think this is something unique for Go. This decision means that even if you don't care about a specific order, you will still need to sort the map before doing something else if you want reproducibility, something that I care a lot.</p> <p>The fix for this? Go could offer an ordered map implementation inside the standard library. An ordered map ensure that the iteration order of the map is the same as the insertion order (that is, by the way, a powerful property that allow maps to be used in other contexts, not just my pet peeve above).</p> <p>Python actually does this for any dictionaries since <a href="https://stackoverflow.com/a/39980744">Python 3.6</a>, but it offered an <a href="https://docs.python.org/3/library/collections.html#collections.OrderedDict">OrderedDict</a> before it (and <code>OrderedDict</code> still has some methods that normal <code>dict</code> doesn't, that maybe useful in specific cases).</p> <p>Before generics it would be impossible to have a type-safe API for such data structure without introducing a new data type in the language (like <code>slices</code>), but now Go has generics so it is not an issue anymore. The other issue is that you would be forced to iterate manually in this new data structure, but thanks to the new <a href="https://tip.golang.org/doc/go1.23#language"><code>range-over-func</code></a> in Go 1.23, it means we can iterate in an ordered map as a library almost exactly like we can do as a <code>map</code>:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;orderedmap&#34;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() { </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">orderedmap</span>.<span style="color:#a6e22e">New</span>[<span style="color:#66d9ef">string</span>, <span style="color:#66d9ef">bool</span>]() </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;foo&#34;</span>, <span style="color:#66d9ef">true</span>) </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;bar&#34;</span>, <span style="color:#66d9ef">false</span>) </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;baz&#34;</span>, <span style="color:#66d9ef">true</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">k</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Iterator</span>() { </span></span><span style="display:flex;"><span> println(<span style="color:#a6e22e">k</span>) <span style="color:#75715e">// Order always will be: foo, bar, baz </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><p>Now, of course the lack of Ordered Map in the standard library can be filled with third party implementations, e.g.: I am using this <a href="https://github.com/elliotchance/orderedmap">one</a> in one of my projects. But being in standard library reduces the friction: if there was some implementation in standard library, I would generally prefer it unless I have some specific needs. However when the standard library doesn't offer what I need, I need to find it myself a suitable library, and this ends up taking time since generally there are lots of alternatives.</p> <h2>Keyword and default arguments for functions</h2> <p>Something that comes straight from Python that I miss sometimes in Go is that you can do things like this when declaring a function:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">hello</span>(name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;World&#34;</span>): </span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Hello, </span><span style="color:#e6db74">{</span>name<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>hello(<span style="color:#e6db74">&#34;Foo&#34;</span>) <span style="color:#75715e"># &#34;normal&#34; function call</span> </span></span><span style="display:flex;"><span>hello(name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;Bar&#34;</span>) <span style="color:#75715e"># calling with keyword arguments</span> </span></span><span style="display:flex;"><span>hello() <span style="color:#75715e"># calling with default arguments</span> </span></span></code></pre><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span>$ python hello.py </span></span><span style="display:flex;"><span>Hello, Foo </span></span><span style="display:flex;"><span>Hello, Bar </span></span><span style="display:flex;"><span>Hello, World </span></span></code></pre><p>The lack of default arguments especially affects even some of the API decisions for Go standard library, for example, <code>string.Replace</code>:</p> <blockquote> <p><code>func Replace(s, old, new string, n int) string</code></p> <p>Replace returns a copy of the string s with the first n non-overlapping instances of old replaced by new. If old is empty, it matches at the beginning of the string and after each UTF-8 sequence, yielding up to k+1 replacements for a k-rune string. If n &lt; 0, there is no limit on the number of replacements.</p> </blockquote> <p>If Go had default arguments, <code>Replace</code> could have e.g.: <code>func Replace(s, old, new string, n int = -1)</code> signature, that would mean by default it would always replace every instance of the <code>s</code> string, something that is generally expected by default.</p> <h2>Nullability (or nillability)</h2> <p>I talked I little about this in <a href="https://github.com/thiagokokada/blog/blob/main/2024-07-29/02-go-a-reasonable-good-language.md">my previous post about Go</a>, but I want to expand here.</p> <p>First, I don't think the language needs to support the generic solution for nullability, that would be either having proper Union or Sum types. Kotlin AFAIK doesn't support neither, but my 2 years experience with Kotlin showed that just having nullable types already helped a lot in ensuring type safety.</p> <p>Second, I do feel that Go has less issues with <code>nil</code> values, than say, Java, because its decision of using zero values instead of <code>nil</code> in many cases. So for example, a string can never be <code>nil</code>, however a string pointer can be. This means that this is fine:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">s</span> <span style="color:#66d9ef">string</span>) { </span></span><span style="display:flex;"><span> <span style="color:#75715e">// do something with s </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} </span></span></code></pre><p>However:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#66d9ef">string</span>) { </span></span><span style="display:flex;"><span> <span style="color:#75715e">// s maybe nil here, better check first </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} </span></span></code></pre><p>Still, I get more <code>panic</code> for <code>nil</code> pointer deference than I get in other languages that offer nullables (heck, even Python with <a href="https://www.mypy-lang.org/"><code>mypy</code></a> is safer).</p> <p>Sadly this is the change in this post that is more likely to need a completely new revision of the language. <a href="https://github.com/golang/go/issues/49202">nillability</a> was proposed before, but it is really unlikely it can be done without breaking backwards compatibility.</p> <p>It could be done the Java way by adding a <code>nullable</code> type to the standard library (<a href="https://jcp.org/en/jsr/detail?id=305">JSR305</a>), but the fact that <a href="https://stackoverflow.com/questions/2289694/what-is-the-status-of-jsr-305">JSR305 is considerd dead</a> by many shows how difficult it is to do something like this without a major change in the language. Dart is the only language that I know that <a href="https://dart.dev/null-safety/understanding-null-safety">did this successfully</a>, but definitely it was not without its pains. And the fact that most people that program in Dart probably does because of Flutter (that eventually required newer versions with null-safety) is not a good sign.</p> + <p>I like Go. I think it is a <a href="https://github.com/thiagokokada/blog/blob/main/2024-07-29/02-go-a-reasonable-good-language.md">reasonable good language</a>, and has some good qualities that makes up for its flaws. However, this doesn't mean I think the language couldn't be better, far from it.</p> <p>This blog post is a list of things that I miss from Go from other languages. Some of the things here could probably be implemented soon, some other would probably need a major revision of the language. The list is unordered, because this makes it easier for me to update in the future if I found something else, but also because I don't want to think too hard about giving each point here a rank.</p> <p>With all above, let's start.</p> <h2>Ordered maps in standard library</h2> <p>When I first learned about <a href="https://docs.python.org/3/library/stdtypes.html#typesmapping">dictionaries</a> in Python it quickly became one of my favorite data structures ever. They're extremely versatile, and most modern programming languages have something similar in its standard library. Go isn't different, it has <a href="https://go.dev/blog/maps"><code>map</code></a>, that is Go implementation of a <a href="https://en.wikipedia.org/wiki/Hash_table">hash table</a>. However <code>map</code> in Go are quirky, for example:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() { </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">bool</span>{<span style="color:#e6db74">&#34;foo&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;bar&#34;</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">&#34;baz&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;qux&#34;</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">&#34;quux&#34;</span>: <span style="color:#66d9ef">true</span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">k</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">m</span> { </span></span><span style="display:flex;"><span> println(<span style="color:#a6e22e">k</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span>$ go run ./test.go </span></span><span style="display:flex;"><span>bar </span></span><span style="display:flex;"><span>baz </span></span><span style="display:flex;"><span>qux </span></span><span style="display:flex;"><span>quux </span></span><span style="display:flex;"><span>foo </span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"> </span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>$ go run ./test.go </span></span><span style="display:flex;"><span>foo </span></span><span style="display:flex;"><span>bar </span></span><span style="display:flex;"><span>baz </span></span><span style="display:flex;"><span>qux </span></span><span style="display:flex;"><span>quux </span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"> </span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>$ go run ./test.go </span></span><span style="display:flex;"><span>qux </span></span><span style="display:flex;"><span>quux </span></span><span style="display:flex;"><span>foo </span></span><span style="display:flex;"><span>bar </span></span><span style="display:flex;"><span>baz </span></span></code></pre><p>Now, I don't expect any hash table implementation to keep the order of the elements, but Go actually <a href="https://victoriametrics.com/blog/go-map/">randomise each map instance</a>:</p> <blockquote> <p>But here’s the deal, while the hash function used for maps in Go is consistent across all maps with <strong>the same key type</strong>, the <code>seed</code> used by that hash function is different for each map instance. So, when you create a new map, Go generates a random seed just for that map.</p> </blockquote> <p>While I understand the reason for this (i.e.: to avoid developers relying in a specific iteration order), I still find it weird, and I think this is something unique for Go. This decision means that even if you don't care about a specific order, you will still need to sort the map before doing something else if you want reproducibility, something that I care a lot.</p> <p>The fix for this? Go could offer an ordered map implementation inside the standard library. An ordered map ensure that the iteration order of the map is the same as the insertion order (that is, by the way, a powerful property that allow maps to be used in other contexts, not just my pet peeve above).</p> <p>Python actually does this for any dictionaries since <a href="https://stackoverflow.com/a/39980744">Python 3.6</a>, but it offered an <a href="https://docs.python.org/3/library/collections.html#collections.OrderedDict">OrderedDict</a> before it (and <code>OrderedDict</code> still has some methods that normal <code>dict</code> doesn't, that maybe useful in specific cases).</p> <p>Before generics it would be impossible to have a type-safe API for such data structure without introducing a new data type in the language (like <code>slices</code>), but now Go has generics so it is not an issue anymore. The other issue is that you would be forced to iterate manually in this new data structure, but thanks to the new <a href="https://tip.golang.org/doc/go1.23#language"><code>range-over-func</code></a> in Go 1.23, it means we can iterate in an ordered map as a library almost exactly like we can do as a <code>map</code>:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;orderedmap&#34;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() { </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">orderedmap</span>.<span style="color:#a6e22e">New</span>[<span style="color:#66d9ef">string</span>, <span style="color:#66d9ef">bool</span>]() </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;foo&#34;</span>, <span style="color:#66d9ef">true</span>) </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;bar&#34;</span>, <span style="color:#66d9ef">false</span>) </span></span><span style="display:flex;"><span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;baz&#34;</span>, <span style="color:#66d9ef">true</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">k</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">Iterator</span>() { </span></span><span style="display:flex;"><span> println(<span style="color:#a6e22e">k</span>) <span style="color:#75715e">// Order always will be: foo, bar, baz </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><p>Now, of course the lack of Ordered Map in the standard library can be filled with third party implementations, e.g.: I am using this <a href="https://github.com/elliotchance/orderedmap">one</a> in one of my projects. But being in standard library reduces the friction: if there was some implementation in standard library, I would generally prefer it unless I have some specific needs. However when the standard library doesn't offer what I need, I need to find it myself a suitable library, and this ends up taking time since generally there are lots of alternatives.</p> <h2>Keyword and default arguments for functions</h2> <p>Something that comes straight from Python that I miss sometimes in Go is that you can do things like this when declaring a function:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">hello</span>(name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;World&#34;</span>): </span></span><span style="display:flex;"><span> print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Hello, </span><span style="color:#e6db74">{</span>name<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>hello(<span style="color:#e6db74">&#34;Foo&#34;</span>) <span style="color:#75715e"># &#34;normal&#34; function call</span> </span></span><span style="display:flex;"><span>hello(name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;Bar&#34;</span>) <span style="color:#75715e"># calling with keyword arguments</span> </span></span><span style="display:flex;"><span>hello() <span style="color:#75715e"># calling with default arguments</span> </span></span></code></pre><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span>$ python hello.py </span></span><span style="display:flex;"><span>Hello, Foo </span></span><span style="display:flex;"><span>Hello, Bar </span></span><span style="display:flex;"><span>Hello, World </span></span></code></pre><p>The lack of default arguments especially affects even some of the API decisions for Go standard library, for example, <code>string.Replace</code>:</p> <blockquote> <p><code>func Replace(s, old, new string, n int) string</code></p> <p>Replace returns a copy of the string s with the first n non-overlapping instances of old replaced by new. If old is empty, it matches at the beginning of the string and after each UTF-8 sequence, yielding up to k+1 replacements for a k-rune string. If n &lt; 0, there is no limit on the number of replacements.</p> </blockquote> <p>If Go had default arguments, <code>Replace</code> could have e.g.: <code>func Replace(s, old, new string, n int = -1)</code> signature, that would mean by default it would always replace every instance of the <code>s</code> string, something that is generally expected by default.</p> <h2>Nullability (or nillability)</h2> <p>I talked I little about this in <a href="https://github.com/thiagokokada/blog/blob/main/2024-07-29/02-go-a-reasonable-good-language.md">my previous post about Go</a>, but I want to expand here.</p> <p>First, I don't think the language needs to support the generic solution for nullability, that would be either having proper Union or Sum types. Kotlin AFAIK doesn't support neither, but my 2 years experience with Kotlin showed that just having nullable types already helped a lot in ensuring type safety.</p> <p>Second, I do feel that Go has less issues with <code>nil</code> values, than say, Java, because its decision of using zero values instead of <code>nil</code> in many cases. So for example, a string can never be <code>nil</code>, however a string pointer can be. This means that this is fine:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">s</span> <span style="color:#66d9ef">string</span>) { </span></span><span style="display:flex;"><span> <span style="color:#75715e">// do something with s </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} </span></span></code></pre><p>However:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#66d9ef">string</span>) { </span></span><span style="display:flex;"><span> <span style="color:#75715e">// s maybe nil here, better check first </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} </span></span></code></pre><p>Still, I get more <code>panic</code> for <code>nil</code> pointer deference than I get in other languages that offer nullables (heck, even Python with <a href="https://www.mypy-lang.org/"><code>mypy</code></a> is safer).</p> <p>Sadly this is the change in this post that is more likely to need a completely new revision of the language. <a href="https://github.com/golang/go/issues/49202">nillability</a> was proposed before, but it is really unlikely it can be done without breaking backwards compatibility.</p> <p>It could be done the Java way by adding a <code>nullable</code> type to the standard library (<a href="https://jcp.org/en/jsr/detail?id=305">JSR305</a>), but the fact that <a href="https://stackoverflow.com/questions/2289694/what-is-the-status-of-jsr-305">JSR305 is considerd dead</a> by many shows how difficult it is to do something like this without a major change in the language. Dart is the only language that I know that <a href="https://dart.dev/null-safety/understanding-null-safety">did this successfully</a>, but definitely it was not without its pains. And the fact that most people that program in Dart probably does because of Flutter (that eventually required newer versions with null-safety) is not a good sign.</p> <h2>Lambdas</h2> <p>Go is a surprising good language for some functional code, thanks to having first class functions and closures. Sadly the syntax doesn't help, since the only way you can use anonymous functions in Go is using <code>func</code>. Especially if the types are complex, this can result in some convoluted code. Take the examples from the <a href="https://go.dev/wiki/RangefuncExperiment"><code>range-over-func</code> experiment</a> for example:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">slices</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Backward</span>[<span style="color:#a6e22e">E</span> <span style="color:#a6e22e">any</span>](<span style="color:#a6e22e">s</span> []<span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">bool</span>) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">yield</span> <span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">bool</span>) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> len(<span style="color:#a6e22e">s</span>)<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">--</span> { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">yield</span>(<span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">s</span>[<span style="color:#a6e22e">i</span>]) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><p>If Go had a syntax for lambdas, especially if we could elide the types, this could be simplified a lot:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">slices</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Backward</span>[<span style="color:#a6e22e">E</span> <span style="color:#a6e22e">any</span>](<span style="color:#a6e22e">s</span> []<span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">bool</span>) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#a6e22e">yield</span>) =&gt; { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> len(<span style="color:#a6e22e">s</span>)<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">--</span> { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">yield</span>(<span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">s</span>[<span style="color:#a6e22e">i</span>]) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><p>Or even something like this would already help, no special syntax but allowing the types to be elided in an unnamed function:</p> <pre tabindex="0" style="color:#f8f8f2;background-color:#272822;"><code><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">slices</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Backward</span>[<span style="color:#a6e22e">E</span> <span style="color:#a6e22e">any</span>](<span style="color:#a6e22e">s</span> []<span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">func</span>(<span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">E</span>) <span style="color:#66d9ef">bool</span>) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">yield</span>) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> len(<span style="color:#a6e22e">s</span>)<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">--</span> { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">yield</span>(<span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">s</span>[<span style="color:#a6e22e">i</span>]) { </span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre><p>This feature I still am somewhat hopeful that may become a reality in some future version of the language, since they didn't close the <a href="https://github.com/golang/go/issues/21498">issue</a> yet, and the discussion about the possibility of this feature is still ongoing.</p> https://github.com/thiagokokada/blog/blob/main/2024-08-17/01-an-unordered-list-of-things-i-miss-in-go.md Sat, 17 Aug 2024 00:00:00 +0000