Axel Wagner writes about Go, including generics, type sets, error handling, and backward compatibility, plus broader programming language topics.
HN commenters cite it for explaining Go design decisions, such as variance and generics constraints, and for its essay on diminishing returns of static typing.
Cancellation is a little tricky. If things can cancel at any point, then it's impossible to write safe/correct code. Async has the advantage that the await calls are natural sync points, so it's (probably) safe to cancel there. But historical experience has also shown that people will forget to yield when they should, and it will lead to cooperation problems.
I think the best approach has to look something like Go's, but perhaps a bit more structured (dynamic scoping[1] might help perhaps with task nurseries[2]). Unless you're writing extremely low level code, you want your language runtime to intercept all syscalls and figure out the async story for you. The language should handle making sure that the M:N mapping works out, no one opens a socket the wrong way etc. Then for you as the program writer, your responsibility is just setting explicit cancellation points as part of the general error handling approach. It's still not perfect, but I think that would be the next evolution from what exists today.
[1] https://blog.merovius.de/2017/08/14/why-context-value-matters-and-how-to-improve-it.html
[2] https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/
> You need to have something that uses those templates.
Exactly. That is what I said:
> because you need to know the actual type arguments used, regardless of what the constraints might say.
It is because type-checking concept code is NP complete - it is trivial to check that a particular concrete type satisfies constraints, but you can not efficiently prove or disprove that all types which satisfy one constraint also satisfy another. Which you must do to type-check code like that (and give the user a helpful error message such as “this is fundamentally not satisfiable, your constraints are broken”).
And it’s one of the shortcomings of C++ templates that Go was consciously trying to avoid. Go’s generics are intentionally limited so you can only express constraints for which you can efficiently do such proofs.
I described the details a while back: https://blog.merovius.de/posts/2024-01-05_constraining_complexity/
I'm a fan of this multi-language trend of gradual and optional typing. Static typing has definite benefits, but there are also diminishing returns on productivity as a language becomes more and more statically safe. Often times, type safety is a rabbit hole that can easily turn a language into what some call a "puzzle language," where the efforts to please the compiler become non-intuitive and take long times to figure out. Some of the classes in Swift for example have to adopt a hundred protocols just to compile. Rust is an even more extreme example. You might get extra safety, but at significant development cost.
Gradual typing in Python is a happy medium that lets the developer decide (to some degree) on the workflow up front. It's a nice compromise for a wide range of work.
I love this explanation from a few months ago:
https://blog.merovius.de/2017/09/12/diminishing-returns-of-static-typing.html
I'm surprised by the complexity of Go's generic constraints, given the language's focus on simplicity. Things like the difference between "implementing" and "satisfying" a constraint [0], and exceptions around what a constraint can contain [1]:
> A union (with more than one term) cannot contain the predeclared identifier comparable or interfaces that specify methods, or embed comparable or interfaces that specify methods.
Is this level of complexity unavoidable when implementing generics (in any language)? If not, could it have been avoided if Go's design had included generics from the start?
[0] https://stackoverflow.com/questions/77445861/whats-the-difference-between-types-implements-and-types-satisfies
[1] https://blog.merovius.de/posts/2024-01-05_constraining_complexity/
>I have never got all these jokes.
If you mean that literally, this joke is referencing another joke meme in the functional programming community, explained here: https://blog.merovius.de/2018/01/08/monads-are-just-monoids.html
If you mean, you don't get why people joke about Git being hard, it probably isn't for most professional programmers already accustomed to some kind of source control, just perhaps to anyone new to it.
I recall reading some details around this on https://blog.merovius.de. Maybe it was this article: https://blog.merovius.de/posts/2018-06-03-why-doesnt-go-have-variance-in. Compilation speed plays a big factor when deciding which features are added to Golang and I think they'd have a hard time maintaining the current compilation speed if they remove this limitation.