Generics can make your Go code slower
Go 1.18 is here, and with it, the first release of the long-awaited implementation of Generics is finally ready for production usage. Generics are a frequently requested feature that has been highly contentious throughout the Go community. On the one side, vocal detractors worry about the added complexity. They fear the inescapable evolution of Go towards either a verbose and Enterprisey Java-lite with Generic Factories or, most terrifyingly, a degenerate HaskellScript that replaces ifs with Monads. In all fairness, both these fears may be overblown. On the other side, proponents of generics believe that they are a critical feature to implement clean and reusable code at scale.
This blog post does not take sides in that debate, or advise where and when to use Generics in Go. Instead, this blog post is about the third side of the generics conundrum: It’s about systems engineers who are not excited about generics per se, but about monomorphization and its performance implications. There are dozens of us! Dozens! And we’re all due for some serious disappointment.
node.example.com Is An IP Address
This takes a bit to get to the punchline, but man, good old duck typing for the win.
It turns out that, under certain conditions, the ipaddress module can create IPv6 addresses from raw bytes. My assumption is that it offers this behavior as a convenient way to parse IP addresses from data fresh off the wire.
Does node.example.com meet those certain conditions? You bet it does. Because we’re using Python 2 it’s just bytes and it happens to be 16 characters long.
Implementing a Type-safe printf in Rust
I show how to use heterogeneous lists and traits to implement a type-safe printf in Rust. These mechanisms can ensure that two variadic argument lists share important properties, like the number of format string holes matches the number of printf arguments.
Making Illegal States Unrepresentable
It’s a concept I find very helpful. But if you look for examples online almost everything either “let’s prevent dividing-by-zero” or “let’s enumerate the cases in a data type”. We can more creative than that! Some examples of “illegal states unrepresentable” that I found useful but have not seen anyone else talk about online:
Porting to TypeScript Solved Our API Woes
With the Ruby backend, we sometimes forgot that a particular API property held an array of strings, not a single string. Sometimes we changed a piece of the API that was referenced in multiple places but forgot to update one of those places. These are normal dynamic language problems in any system whose tests don’t have 100% test coverage. (And it will still happen even with 100% coverage; it’s just less likely.)
Dynamically scoped variables in Go
What we want is to be able to access a variable whose declaration is neither global, or local to the function, but somewhere higher in the call stack. This is called dynamic scoping. Go doesn’t support dynamic scoping, but it turns out, for restricted cases, we can fake it.
How can I have a C++ function that returns different types depending on what the caller wants?
Fighting the Async fragmentation
This is about some dead ends when trying to fix the problem of Rust’s async networking fragmentation. I haven’t been successful, but I can at least share what I tried and discovered, maybe someone else is having the same bugging feeling so they don’t have to repeat them. Or just maybe some of the approaches would work for some other problems. And because we have a bunch of success stories out there, having some failure stories to balance it doesn’t hurt.
The story of a V8 performance cliff in React
Models of Generics and Metaprogramming: Go, Rust, Swift, D and More
In some domains of programming it’s common to want to write a data structure or algorithm that can work with elements of many different types, such as a generic list or a sorting algorithm that only needs a comparison function. Different programming languages have come up with all sorts of solutions to this problem: From just pointing people to existing general features that can be useful for the purpose (e.g C, Go) to generics systems so powerful they become Turing-complete (e.g. Rust, C++). In this post I’m going to take you on a tour of the generics systems in many different languages and how they are implemented. I’ll start from how languages without a special generics system like C solve the problem and then I’ll show how gradually adding extensions in different directions leads to the systems found in other languages.
Detecting in C++ whether a type is defined
How 2 TypeScript: Get the last item type from a tuple of types
Kinda like a normal array lookup!
But what if you don’t know the length of the tuple? Hmm... how do we get TypeScript to tell us the length and then let us use that length to pick out the last item, all at compile time?
What should you do if somebody passes a null pointer for a parameter that should never be null? What if it’s a Windows Runtime class?
If you put the cases of in-process and out-of-process callers together, you see that the conclusion is “Go ahead and dereference those pointers.” If the caller is in-process, then it’s okay to crash because you are crashing the caller’s process (which happens to be the same process that you are in). If the caller is out-of-process, then the RPC layer will prevent invalid null pointers from getting through.
There’s an additional wrinkle to this general principle, however, for the case where you are implementing a Windows Runtime class.
Go has no type for types in the language
Part of what this means is that in Go, you cannot write an expression like ‘x := y.(type)’ not just because the language syntax forbids it, but because there is no standard type that the variable x can be. If you wanted to allow this, you would have to create a new Go type and define what its behavior was.
A new runtime for Nim
In this blog post I explore how the full Nim language can be used without a tracing garbage collector. Since strings and sequences in Nim can also be implemented with destructors the puzzle to solve is what to do with Nim’s ref pointers and new keyword.
Type-specific allocation turns every “use after free” bug into a logical bug but no memory corruption can happen. So ... we have already accomplished “memory safety without a GC”. It didn’t require a borrow checker nor an advanced type system. It is interesting to compare this to an example that uses array indexing instead of pointers:
.NET Internals Cookbook
In this series I answer various .NET questions. Some of them are asked during interviews, some of them I see on the internet, some of them are completely made up. The goal is to provide short answer with links to references if needed. This is by no means a .NET tutorial or experts reference, this is just a bunch of useful answers to refresh your knowledge.
Some of this gets pretty deep actually.
Typed nils in Go 2
This is an experience report about a gotcha in Go that catches every Go programmer at least once. The following program is extracted from a larger version that caused my co-workers to lose several hours today.
You’ve probably realised the cause of this problem is the dreaded typed nil, a gotcha that has its own entry in the Go FAQ. The typed nil emerges as a result of the definition of a interface type; a structure which contains the concrete type of the value stored in the interface, and the value itself.
Typed nils are an entirely logical result of the way dynamic types, aka interfaces, are implemented, but are almost never what the programmer wanted.
The linked post is a good read as well: https://research.swtch.com/interfaces
to increment some counter on the page
node.innerText += 1 doesn’t work (0 → 01 → 011 → ⋯), but node.innerText -= -1 works fine (0 → 1 → 2 → ⋯)
However, newValue is returned, not the value that was actually stored in the array!
C++ Standard Ranges
As you may have heard by now, Ranges got merged and will be part of C++20. This is huge news and represents probably the biggest shift the Standard Library has seen since it was first standardized way back in 1998.
Future blog posts will discuss how we got here and the gritty details of how the old stuff and the new stuff play together (we’re C++ programmers, we love gritty details), but this post is strictly about the what.