First paragraph: "I don't like it". Second paragraph: snide comment. Third paragraph: "I don't like it". Fourth paragraph: "Rust is better". Fifth paragraph: snide comment.
That's the Go mistake, the one that causes most of the issues for the intended audience, the one that can't really be fixed. It's a shame Pike doesn't really discuss this, even if it's hopeless now.
The rest is just people projecting and self-selecting outside of the intended audience. Don't like it, don't use it, we don't all need to agree with you.
The design decisions around zero values infect protobufs too, and they suck to work around. The fact that an empty message can successfully deserialise into any valid protobuf is an insane decision and should have been thrown out long ago.
The reason for this is that the protobuf wire format is designed for very high entropy: It contains only a minimal amount of metadata and consists mostly of data. This means you can deserialize most wire messages as a different message. This is a tradeoff: smaller message size for loss of schema information. This just means that schemas need to be handled at a higher level. This tradeoff makes some sense if you process millions of protos per second.
BTW: Dismissing a tradeoff like this as insane is derogatory. You can do better
Use pointer variables ? Works 4 me.
One could have easily added an Optional type that forces programmers to check for nil every time the optional variable is accessed.
type Optional[V any] struct { IsSet bool Value V }
Interfaces and how they ended up limiting generics (parametric polymorphism) are a tradeoff. Structural interfaces (duck-typed in an otherwise static-typed, with composition-over-inheritance, language) are innovative, interesting, and offer many benefits, enough to compensate any drawbacks. This is mentioned in the talk.
But the fact that anyone can just conjure a zero value out of thin air, and this fine because it's zero initialized (a decade after Java had proved this was really not good enough) it's pretty inexcusable. And this is not just a "default," it's actually impossible by design to enforce initialization in any way.
Then they did this in a language with pointers, which by necessity are zero initialized to nil, and simply added some timid steps to make nils more useable/useful (like nil receivers being valid). Which unfortunately, in the end, only further complicates static analysis and tooling that might ameliorate the issue.
Finally, if this wasn't enough of a problem, nil panics (any panics, in fact) are a hard crash if a goroutine doesn't handle them, and: it's impossible to add a global handler, it's impossible to prevent goroutines from being created that don't handle panics. So any code that you call can crash your program, and this is considered good form.
If you really feel that zero values are useful enough to justify all this, please explain. Because I just don't see it. This isn't a widely innovative feature that shapes idiomatic programming in an amazing way. The standard library is full of awkward hacks to make zero values useful (esp. in the face of backwards compatibility), where simple enforced construction would be much better.
I love Go. It's my favourite programming tool. I wish I could use it more professionally. But not acknowledging this error, or being dismissive, and doing nothing about it, helps no one.
by paragraph:
1) The article should have acknowledged the issues that a wide part of the community are experiencing.
2) I accept that I'm the problem. But then I must leave.
3) The consequence of their attitude is that I cannot recommend the language anymore. I used to be excited about it, and that disappointment makes me angry.
4) If only Go was trying to be better. Rust is just an example of the visionary leadership that I expect from Go. I want go to be visionary! Because they have some things right, like fast compiliation, cross compilation, simple syntax, and a focus on simple concurrency. But it's like those ideas never developed.
5) Rust is a counterexample, that a language can be visionary, without giving up on its fundamentals.
6) Acknowledgment that Go was the best solution at a time. But also that the time seems to have passed.
Not to be flippant, but I've often heard Go described as taking the programming language advances over the last 50 years and throwing them away.
That's by design, right? Go is very opinionated. They looked at other languages that they hated, e.g., C++/Java and didn't want to replicate them. But then adding their own mistakes along the way.
The brand new mistake that surprised me was that nil does not always equal nil. So just checking for nil is not enough sometimes, one has to "cast" as nil to the type you're expecting. And goland doesn't catch it. C++/Java/Python/C, null always equals null. But not in golang. shrug
The primary issue I have is that go doesn't make it very easy to write unit tests. You have to use interfaces everywhere just to inject your mocks.
I feel like that's the big mistake they made. Any new language needs to make it super easy to write unit tests without forcing major design decisions that affects development.
If you don't believe me, take a project like k8s and starting from its inception, you can see dozens of issues where people have to explain function returns a nil interface and not a nil value. This is explained a stupendous number of times.
Now repeat this for thousands of Go projects.
Of-course you can claim this is not an issue in the same way that anyone can claim that buffer overflows are not an issue in C/C++ for "real" programmers.
Obviously, it would be better if Go didn't have this problem. If I made Go 2, it would have blank interface be "none" and blank pointer be "null". Even better, I would add a reference type that can't be null and sum types or something like that. But these things are all relative.
In Python, people using "is" for numbers is a problem. But in practice only very junior developers do that and it mostly gets caught in dev. There was the Digg outage caused by a default argument being [] instead of None https://lethain.com/digg-v4/ and that I see as a slightly more serious bug that can slip by without good code review.
Every language has pitfalls and the question is whether they outweigh the other benefits.
Yes, in my case anyway. That code didn't have any unit tests around it. Goland didn't flag it. It's a pattern that was "surprising" to me at least. Particularly since it breaks with tradition.
I consider Goland to be invaluable when coding go. But still, it's not perfect.
> There was the Digg outage caused by a default argument being [] instead of None
FWIW Pycharm flags this now, and can fix this automatically. Not sure about VSCode.
I guess the point I'm making is IDE's should find the novice mistakes, and then a lot of my complaints about languages go away.
It’s a pity there’s no other languages in the world that they could have taken good design from, rather than looking at a handful of languages they didn’t like and basically throwing the baby out with the bath water.
C with garbage collection that compiled natively and had a sane FFI to C would be a good addition. Add a decent library to get work done quickly and you've got a great solution.
That's what it felt like golang was trying to do. I do think it's easier to pick up than C. But it's not something I would say, you must write your code in.
To be more controversial, its flaws remind me of COBOL, both in the flawed belief that a lack of expressive power makes your programs easier, rather then harder, to read, and in the sense that both languages seemed completely ignorant of any programming language design ideas outside of their bubble, and just stayed stuck in the past
[1] As of 2022. Prior to that it famously didn't even have generics
I guess the compiler authors don't comment out code?
And it's intentionally not optional on the compiler.
So you have to modify the source and compile your own compiler to disable it. It's ridiculously sadistic to their users.
> Also, there has already been plenty of discussion about things such as where the newlines go, how nil works, using upper case for export, garbage collection, error handling, and so on. There are certainly things to say there, but little that hasn't already been said.
I've worked with perhaps half a dozen real "language enthusiasts" in my time. People who spend lots of time obsessing over "perfect" language design and non-mainstream languages. People who never stick to a language for more than a year or three, who insist everyone else accommodate their current fascination with some language, and who leave behind codebases full of code that will be hard to maintain because they are a patchwork of languages. Not caring that the organization then has to take the time both train people in thoselanguages and ensure they have enough people experienced with the language to be able to use a portion of their time to train newbies.
On a few occasions I actually researched their job history and found that these people had a tendency to make a lot of noise, but produce very little of consequence. They'd find jobs at the outskirts of projects where it would be easier to indulge in their interests without clashing with the principals. Most of their code would be gone just months after they left the position.
My advice is that if you care about building stuff, don't hire language enthusiasts.
In the 8 or so years I've used Go for production software, it hasn't actually turned out to be a big problem compared to my experience with C and C++. If it was a huge problem it should have manifested as such. But in my experience, nil errors are extremely rare in our production code.
If this hadn't been the case, we would probably have invested in Rust. But I can't deny that when I look at those of my friends who use Rust, I'm not exactly impressed with what they accomplish. Even a couple of years in, I see them spending a lot of time fighting with the compiler, having to backtrack and re-think what they are doing or, sometimes, having to rewrite/replace code (sometimes third party) that isn't as strict as they wish to be.
Is it worth the extra effort?
That being said, sure, I think Go would be less of a "getting things done" if the maintainers had felt an obligation to add every checklist item to the language from the start. As someone else pointed out, the fact that they had the guts to say no to a bunch of things was a really good thing. Just throwing all your favorite ingredients in the pot and stirring it doesn't guarantee you'll end up with a delicious dinner. There's a lot more to it than that.
Why do you think so?
If you don't know what `defer` execution order is LIFO, you cannot read code with multiple `defer`'s. IMO, Rust just have more things like `defer`
In my opinion, Rust is a prime example of overengineering (just like C++).
nil/null is really problematic, true. But how languages handle this otherwise? Is it that program must be statically analyzed to ensure that no nil/null path exists or there are other solutions as well?
Would be thankful for any pointers.
All approaches come down to modeling it in the type system instead of it being invisible.
One approach is modeling it as a sum type [1]. A sum type is a type that models when it's one thing OR another thing. Like it's "a OR b". "null OR not null". So a lot of languages have a type called `Maybe<T>` which models it's a "T OR Nothing". Different languages use different names (like `Option` [2]) but it's the same idea. The key is that null is now modeled using normal / non-special / user-defined types so that it's no longer invisible.
Another approach is using so-called "nullable types" and flow typing [3]. For example, `Foo` is a normal type and `Foo?` is a nullable version of `Foo`. You're not allowed to pass a `Foo?` to a function that requires a `Foo` (the other way is fine). When doing a null check, the compiler narrows a `Foo?` to a `Foo` inside the non-null branch of the check. This is one capability of a more general compiler/language technique sometimes referred to as "narrowing" [4] or "static flow analysis" [5]
[0] https://yourbasic.org/golang/gotcha-why-nil-error-not-equal-...
[1] https://en.m.wikipedia.org/wiki/Tagged_union
[2] https://doc.rust-lang.org/std/option/index.html
[3] https://kotlinlang.org/docs/null-safety.html
[4] https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...
[5] https://learn.microsoft.com/en-us/dotnet/csharp/nullable-ref...
That's nice, but also rather self-congratulatory. I was expecting some kind of acknowledgment of the deeper issues in the language. But perhaps that's the central issue, that the language is perfect in their eyes. I'm the problem.
Well, okay then.
I can't recommend the language, because of its type system, the error handling, the unsafe concurrency, the simplistic syntax, nil, default zero, and a large number of mainstream packages are abandoned.
I now use Rust as my main language. It has a flourishing ecosystem and is visionary in so many ways that Go is not.
Put more pointedly, I'm sure Go had its day, when it was competing with PHP as a backend language.