Hacker News new | ask | show | jobs
by nu11ptr 1356 days ago
I really like the engineering principles in general that the Go team uses, however, I just don't like Go. That isn't meant as a slight or anything other than simply my opinion. That said, I really like the idea of a simple language based on the sort of principles demonstrated here. The runtime seems really nice, I just wish I liked the language better (IMO: not expressive enough, needs better error handling, needs much better enums, needs pattern matching, has error prone C-style 'for', almost everything is statements not expressions, special comments are error prone, has nil pointers, etc.). In the end I think if someone were to write a slightly simpler version of Rust with the Go runtime it might be pretty neat. That said, I don't know what I'd want to drop from Rust so maybe I'm just fantasizing.
15 comments

I haven't found any issues with expression, so far. I wouldn't use it to write a UI, but for writing networking code or automated tasks I find it perfectly suited to the task.

I appreciate it's error handling. It's burdensome, sure, but it presents almost no additional cognitive load when attempting to reason about control flow.

It essentially has no enums. However, it has a comfortable type system that can wrap primitive types, and you can define methods on those wrapped types. It's serviceable, but not elegant.

I have never been a fan of pattern matching outside of functional languages. The phenomenon I notice in languages that do have it, is the majority of uses cases seem to be a pattern match with two outcomes, one a Some() and the other a None(). It really seems like a more annoying way to write if {} else {}.

It has for, but it also has 'range.' It makes up for a lot.

It has nil pointers, which are just a "zero value." It's not uncommon to make it useful and safer in certain contexts. You can request a lookup in a map that is nil. You can ask for the length of a slice that is nil. You get sensible answers still. There are still footguns, but they're all single barrel.. which is _nicer_.

I don't need a perfect language. Good enough with low cognitive load and amazing cross-compile facilities I'll take any day of the week.

I will agree Rust has a higher "cognitive load", but not if you write it every day. I think Rust might be tough to leave and come back say a year later, but Go admittedly would be easy. That said, having written 50K+ code in both languages, I never want to write Go again. Rust on the other hand is all I want to write now. I do wonder how many people who understand Go's limitations (and work around them as you point out above) have truly given Rust a try (takes a few months - can't be done faster). I can't imagine anyone would want to go back tbh, but maybe I'm wrong.

EDIT: In fairness, there is one thing and exactly one thing I miss about Go: compile times. Rust is simply no competition here, but in trade, it is doing a ton of extra work. Trade offs.

> I do wonder how many people who understand Go's limitations (and work around them as you point out above) have truly given Rust a try (takes a few months - can't be done faster)

I have written both languages professionally in various contexts for years. In point of fact, I've been writing Rust since before Rust 1.0 came out, although I didn't get my first Rust job until about 3 years later. They're both excellent languages. I actually didn't learn Go until several years after learning Rust, and at the time I didn't appreciate it like I should have, since I was so excited about Rust. Only after getting professional experience with Rust did I truly begin to appreciate how nice Go is for the things it is good at.

Go is just so much better suited for network-connected services (including but not limited to web backends), and Rust is better suited for batch processing or embedded contexts where no compromise on performance can be made, in my experience. There are lots of reasons for this, but it's off topic from the question, and the answer to the question is that I absolutely do go back to Go. Neither language is a perfect tool for all problems, and I constantly dream about building my own language that learns from the best of both... there is certainly room for improvement on both ends.

I have a similar experience. Used to write a lot of golang, and now I write a lot of Rust. I think I would have pointed out the same issues: auditing Golang network code you can often find easy DoS attacks due to nil values being dereferences (protobuf I’m looking at you), there’s no sum types or enums, etc. Still though, I miss how easy it is to read golang and ramp up on a codebase. Even today I sometimes come across a golang codebase and look for something and I can always easily follow the code. Eventhough I’ve written a lot of Rust, it’s always challenging to ramp up on a Rust codebase, and the language is often abused because it’s too expressive. I’d still say Rust is so much more pleasant to write due to that expressiveness. It really is a double edge sword.
It shouldn't be a Rust vs golang. golang is mostly a somewhat better python, and mostly for writing backend services.

You can compare its domain (to a certain degree) to Java or C#, in which case the latter two are superior due to the reasons mentioned previously (enums, pattern matching, etc.).

Agree, about the "us vs them" mentality. The tribalism has gotten out of hand, though I kind of suspect some of it is corporate interests being behind it to push the illusion its a "winner take all" game and "putting the batteries in the backs" of many of the evangelists.
It feels like the language wars of 90s and 00s all over again, and I personally don't want to go through that cycle again.

I don't want to take sides, I want to enjoy programming and learning, that's all. When zealots zeal, kinder people either leave or go underground.

The problem with rust is explicitly that cognitive load in large codebases. It is much easier to read go code than rust when working on many things. Rust fits great if you don't scale though.
It’s funny how experiences differ; I can’t comment on how Go fares here, but if anything, I find that the compiler takes such a mental load off of my shoulders that Rust is the language I find easiest to make large refactorings in.
It's not about refactoring for me. It's about trying to grok what the heck some library author or coworker was thinking when they went all Architecture Astronaut with the type system and traits. It reminds me of how people go crazy with OO and end up with delegation spread across several files. I already have to hold the problem in my head. I find that Go takes such a mental load off my shoulders that I find it the easiest to grok other's intentions (including my own several months/years later) in.
It may depend on the problem at hand as well. I never really bought this “each line is easier to comprehend” reasoning, because then ad absurdum we would all be writing assembly.

But sure, one is a low level language which can and thus must care about every little detail, while the other is a managed language. (And honestly, mixing the two as if they share the same niche is very off putting)

That's fair. In my mind, they're at least related: if I'm trying to learn about some code, I change stuff and see what breaks.
The differentiating factor is probably prior exposure to C++. If you have used C++ then Rust is so easy and convenient and fun. It takes hideous C++ monstrosities and turns them into easy one liners.

If you have never used C++ and your baseline is Go or JavaScript then it probably looks like a confusing hellscape. You have to learn a whole new kind of type system, a new nomenclature (“Vec”s instead of “Array”s), new concepts like allocators, plus the borrow checker and lifetime annotations, and module/crate system. Former C++ programmers only have to learn the latter half.

> The differentiating factor is probably prior exposure to C++. If you have used C++ then Rust is so easy and convenient and fun. It takes hideous C++ monstrosities and turns them into easy one liners.

Obviously you didn't mean that as a universal rule, but for posterity, I'm a counter-example to that. I learned Rust before learning C++ or Go. If anything, I think this biased me _more_ towards Rust than the others; C++ just kind of felt like a more error-prone, less ergonomic Rust, and Go just felt like it took far too much boilerplate to get anything done.

>If you have never used C++ and your baseline is Go or JavaScript then it probably looks like a confusing hellscape.

I'm not sure this is accurate - have you seen modern TypeScript? I actually find that more confusing than Rust to read.

(FWIW, I was predominantly Python/JS-centric before just going "all in" on Rust. It's not that bad, in my experience - if anything, I find Rust "just works" whereas I got tired of the churn in those environments)

I hate C++ but got used to Rust. It just has a steep learning curve unlike Golang. Takes a while but when you get there it is really pleasant.

On the other hand Golang can be fun in like a week of learning it.

Not really. I come from TypeScript and started writing Rust, and they're quite similar in terms of type expressiveness. With both I feel the same level of comfort of refactoring.

Meanwhile Go doesn't even have algebraic data types. I can't imagine working with a language that doesn't have these kinds of functional features anymore after having gotten used to them.

Async code is way nicer in Go than it is in Rust. It makes up for a lot for me when writing web services. Also Go is much easier to read vs Rust.
> Async code is way nicer in Go than it is in Rust.

That’s because Go does not have async code. It has sync code on an async runtime. Like e.g. Erlang/Elixir.

Exactly don't have to deal with red/blue functions
This is an interesting comment. Is there a webpage where I can read more about this idea?
>Also Go is much easier to read vs Rust.

This is subjective - I for example have no real issues reading Rust code, but find Go to look like utter spaghetti on the screen.

I have to side with those saying Go has greater readability, and is easier to use and learn. I know there is a strong push (and arguably corporate backed) to put Rust in everybody faces or present it as being superior, but we still need to be realistic and honest. Not saying that Rust doesn't have its places where it can shine, just that a lot of people are going to find Go to be easier to deal with overall, and it was partially designed to be that way.
>I know there is a strong push (and arguably corporate backed) to put Rust in everybody faces or present it as being superior

No part of my comment was doing this, and this bit is entirely unnecessary and adds nothing to the comment you wrote except fan religious wars about languages.

Sure. But just due to Go type system being pretty basic I've never came across Go code I could not immediately grock, not so with Rust. Even bugging some pretty savvy Rust devs for help it sometimes took them a decent amount of time to pin down whats going on.
That’s probably because you’re not used to it. I think it’s common knowledge that Golang is easy to read.
Couldn't the same be said of rust though? The biggest barrier, in my experience, is almost always familiarity with the syntax.
And much more error prone.
Depends where someone comes from. If they are from a weakly or dynamically typed language (C, python, Javascript) then Go is quite ok. If you're coming from Haskell, OCaml, or Rust, then sure Go might be frightening.
They're all weakly typed languages but C is statically typed.
I've been surprised how writing Rust becomes smooth with experience (mostly; it's still complex in some areas), however, there's more to a language than just the formal part.

Rust requires designing the memory management (ownership/lifetimes) of a program, which Golang doesn't, so even if/when the cognitive load is (hypothetically) equal, there's an additional, non-trivial demand. This is not something that everybody wants in a project (the given tradeoff may not be worth); for sure, if I had to introduce a language in my team only for tooling purposes (ie. small programs), Rust would not be the most productive choice IMO.

That's where I gravitate recently. I got a few more gray hairs learning and doing async Rust and I love the resulting program's speed and correctness.

But it's not easy to quickly prototype stuff with. Just recently I had to write no less than 7 small prototypes and I gave up on the second one, relearned Golang -- took me an hour -- and finished 3 prototypes in a day.

I tend to go all the way in languages so I can use them freely afterwards. But... In Rust's case I just can't justify the effort. Golang really helps you start off a project faster.

I'll still 100% Rust. I'm working on it every day. But indeed, let's use languages where they are at their best.

Anecdote:

I have had year+ long breaks between writing Rust applications. I have been able to get back into it without issue very quickly. At least one of those times I successfully completed a large, sweeping refactoring of a project of mine that had lay untouched for three years. I can confidently say it would have been extremely difficult to do that same task in other languages I consider myself very proficient in and use daily.

> I do wonder how many people who understand Go's limitations (and work around them as you point out above) have truly given Rust a try (takes a few months - can't be done faster)

This is basically the "Anyone who doesn't love my favorite movie hasn't watched it enough times" argument. Programming language design is a complex space, there are no "correct" opinions.

No, I don't thinkbit is that argument. I think the argument is "Rust has a really steep learning curve". Nor do they imply that everyone who has given Rust 3 months will like it.
I tend to agree with his argument though, I can see how a lot of Golang developers would really benefit and enjoy migrating (I’m one of them) but I think it also depends on the type of applications that you usually write.
For me error handling adds a significant distraction due to high vertical space consumption leading to a lot of extra scrolling.

I wished Go formatter would compress

    if err {
        return err
    }
to

    if err { return err }
Even better would be to extend the syntax to allow

   if err return err
But I am Ok with braces.
Two comments:

There are two reasons to have match: exhaustiveness and destructuring/pattern matching. I don't think there's an if/else parallel to `match result { Ok(t) => { ... }, Err(e) => { ... } }`

The fact that a `nil` value can be made useful (which, AFAICT, means it won't blow up your program?) is not a good enough reason to include them in the language. Rust can do perfectly useful things with, say, `Option<Vec<T>>`, and it's possible to distinguish between `Some(vec![])` and `None`. If you care to ignore that distinction you're free to do `maybe_vec.map(|v| v.len()).unwrap_or_default()`. But IMO it's much better to tell the compiler to unify different cases (`None` and `Some(empty)`) than to have to work around the compiler's unification of those cases when you don't want it. It's always easier to increase entropy than to decrease it. (P.S.: should the sum of a nil list be 0, or NaN, or...?)

The problem isn't null/nil as such, it's when the type system treats null values as legal at compile-time.

But if you look at e.g. Kotlin, or C# with "#nullable enable", it tracks whether a given reference can be null or not. So you write if-else code instead of match, but you have to do that in order to actually do something with a reference.

> The fact that a `nil` value can be made useful (which, AFAICT, means it won't blow up your program?) is not a good enough reason to include them in the language.

"Nil is useful", in Go, means that for expensive objects it's often appropriate to pass nil instead of an empty or defaulted object, and call methods on it that act as if it was an empty object or with some default/no-op behavior. This is most obvious with lists/maps but can be applied to all concrete types.

While I can imagine a language in which `Option<T>` can re-export some of `T`'s method set with a default behavior, in practice I don't know any which actually do this.

> The phenomenon I notice in languages that do have it, is the majority of uses cases seem to be a pattern match with two outcomes, one a Some() and the other a None(). It really seems like a more annoying way to write if {} else {}.

In Rust you write this type of thing as:

  if let Some(name) = order.get_name() {
   println!("Order ready for {name}!");
  } else {
   println!("Order number {} ready!", order.num());
  }
So, it's a destructuring pattern match just written as an if-else. Because we did the match here, we can't forget and end up using name when there wasn't one, the variable only exists when it's bound.
If I may ask, I'm curious about why you wouldn't use it to build a User Interface?
One is Go's module/import system. It's pretty inflexible and the connection between exported functions and types and how they are Capitalized or not does create friction for me when I'm "building up" code bases from scratch. Puzzling about module boundaries and type definitions feels like it would consume more time than it's worth in a UI oriented program.

The other is most UIs are wrappers around other functionality, often in libraries. Working on anything other than pure Go code bases adds additional friction, and dynamic libraries add to that complication considerably.

On the other hand, Go's defining feature: Its CSP model, lifted from Newsqueak, was originally designed for user interfaces.

I believe Go could be a really great fit for UIs, but likely never will be as there is little economic incentive to put in the incredible heavy lifting required to build up the support libraries. There are certainly some small projects trying, but without massive resources backing it, it is a struggle to achieve the full fit and finish that we've come to expect.

We always make this discussion with friends. I don't think every language should tick the boxes the same way.

I also personally like Go a lot. It's filling the gap between C++ and Python for me. If I need something compiled with proper threading support, but C++ would be an overkill, I reach for Go.

Go is designed with a human centric view, IMHO: "Make writing great programs easier rather than design a language with novel/cutting edge features, but with a high cognitive load", and I find it as a noble aim as Rust's guarantees and aspirations.

I understand why people love Rust, but I don't think it's the only one, or the proverbial silver bullet that we finally achieved. Yes, it's nice. Yes, it brings many things to the table, but it's not the final solution for once and for all.

You like Rust? Great, Go for it (unintended pun alert), but I think we need all languages from all paradigms and perspectives.

I find this talk [0] very informative and useful while interacting with other languages and communities. I'm not sharing this with any insinuations towards you, but just find it generally perspective broadening.

[0]: https://www.youtube.com/watch?v=YX3iRjKj7C0 - What Killed Smalltalk could Kill Ruby.

> Go is designed with a human centric view, IMHO

If it were Go's design philosophy, it would have allowed unused variables/imports. Those restrictions are there exactly because they help computers, reducing compilation time. The over-focus of compilation time also stems from monorepos being used by Google, whose purpose is also helping computers.

Fast compilation times are a human-centric goal. The machine couldn't care less if it takes a millennia to compile your program. Only humans care about fast compile times.

But, I'm not sure that is even the primary benefit. The primary benefit is that you won't be incentivized to leave unused imports and variables to litter your code, as I see happen all the time in languages that aren't so strict. The Go team has even stated that they decided there would be no warnings because they've learned that warnings get ignored.

they could have had a flag for people who prefer those issues to be warnings. for me, they slow my debugging down a great deal
Then most (if not all) Go programs wouldn’t compile without that flag.

If something can be abused, it’s going to be abused.

I think it goes both ways. Yes, eliminating unused variables and imports accelerates compilation. Also, compilation speed is a great deal for Go, but keeping the code devoid of unused variables also reduces the cognitive load of humans a lot. Keeping language simple is another feature which helps both ways.
> Go is designed with a human centric view, IMHO: "Make writing great programs easier rather than design a language with novel/cutting edge features, but with a high cognitive load", and I find it as a noble aim as Rust's guarantees and aspirations.

I don’t understand his horrid informal writing style trend. You are clearly not quoting anyone and just providing your own interpretation. So why in the hell are you using quotation marks? In this case you could just ditch the quotation marks altogether since the colon already acts as a separator.

Equally, I don’t understand the horrid assumption about everyone being born in an English speaking country and has English as the native language.

So, why in the hell are you just berating me via a comment box?

I made a mistake, alright, and used quotation marks as a tone modifier, because I know no other way to do that.

However in this case you could just pointed me the right direction without berating, since pointing out the mistake already acts as a kind direction arrow.

> Equally, I don’t understand the horrid assumption about everyone being born in an English speaking country and has English as the native language.

Did I even hint that I was assuming anything about native vs. non-native English speaker? Or blaming non-native speakers? No. Play that wounded ESL violin somewhere else.

> I made a mistake, alright, ...

I said that I think that it is bad style. Alright. I didn’t say that it is wrong. (People are doing it a lot these days so apparently it isn’t wrong.)

There's no blame, I agree, but expecting everyone to know proper use of quotation marks (incl. uncommon ways) pulls in some assumption about proper grammar, hence some long experience with the language. Quotation marks are simple tools, but they are hard to master, because they have nuanced uses too. Getting these things wrong is easy.

> Play that wounded ESL violin somewhere else.

I'm not playing a wounded violin, or any violin for that matter. I'm a double bassist. Jokes aside, I'm not being apologetic, just explaining my position. On the other hand, if reading a comment squarely in your own tone bothers you, you might want to think on that, at least a little.

Your wording of your comment implies that I'm doing something wrong by using quotation marks, and I take a note for that. Why are you so upset because somebody admits that there's a room for improvement?

The only thing I'm not agreeing on is your way of sending the message to this shore, that's it.

The thing is, neither English punctuation, nor Hacker News has many facilities to convey tone while discussing. I like to do that in my writing style to convey feelings and tone of my comment. Quotation marks seemed like a usable way to do that, and I possibly got used to that from literary works I've read, so I copied the method.

However, if a native (or more knowledgeable) person tells me that I'm wrong, I tend to believe them and try to learn the proper way, that's it.

> needs better error handling

First it would need to add error handling before it could look to improve upon it.

I'm not entirely convinced it should. I spend my days in a variety of other languages that have added error handling in various ways and, in my experience, it always ends up making errors unnecessarily difficult to do deal with. I regularly wish the idioms of those languages recognized errors as being core to your application as any other value, not something to treat differently. Go really got things right for the type of software I write.

But not all software is solving the same problems. There are a lot of programs where you don't need to think about errors; where stopping the world is fine if you encounter one. Go is not at all a good fit for these situations. However, I think it is okay for Go to not try to be all things to all people. We already have plenty of other good languages that serve other niches. Right tool for the job and all that.

Just make it so:

varFoo, err := GetFoo()

if err != nil {

    return err
}

Can be written as:

varFoo := GetFoo()?

Just like Rust, everyone would stop complaining about Go error handling. But they have this absolutist position on syntactic sugar, even for something like this that would make the language that much nicer to look at and work with.

While that may look good for a hypothetical example, I'm not sure how beneficial it is in real-world use. If your higher level functions are directly passing errors from your lower level functions then you start to bake implementation details into your abstractions which becomes a nightmare later when your implementation changes and callers are depending on those details. In reality, you need to deal with the error immediately and, if there is no better option, return your own error that describes the problem in a non-implementation-specific way.

It is possible that with other error-related features added to the language you could avoid those traps, but Go doesn't feature those either, so simply adding that construct without thinking about the problem much more deeply doesn't buy you much.

If you are solving a stop the world when you encounter an error-type problem that might be okay, although I'd argue that you may as well panic instead. But, again, Go isn't designed for those problems and I'm not sure it needs to be. There are already plenty of good languages designed for that type of work.

All of these things are better handled by good old exceptions (with optional checking, even). All in all, handling errors is often not possible locally -- there is no reasonable thing to do with a db connection error for example at its immediate caller.

The reasonable thing is perhaps to log it there at most, and bubble it up (possibly wrapped as you mentioned). It can be handled for example by a request handler, by returning a 50_ error.

Casting errors to exceptions (panic/recover) are considered a valid approach in Go within your application logic. Its built-in HTTP handlers will even 500 out of the box if a panic isn't recovered beforehand.

But other layers of abstraction lose their utility value if they start to make assumptions about the caller. Maybe in your web service a 50x stop the world error is all you'll ever need if there is a database error, but the next guy using the code for another purpose could have very different requirements and when you have to actually deal with errors, exceptions become a royal pain very quickly. As such, the official line is simply that you shouldn't let errors casted to exceptions cross package boundaries.

But, again, Go isn't really designed for stop the world programming and it's okay to use another tool if your problem space is suited to stopping the world.

I don’t get what you mean by stopping the world. An exception (in most languages) only interrupts the executing thread.
By the way, Vlang (https://github.com/vlang/v/blob/master/doc/docs.md), uses "?" as well with error handling. To include it had/has a number of Go wishlist features.

The nature and culture of Go makes it harder to change direction and be as amenable, but the merits of such conservatism is a "depends" type of thing and can mean incorporating things many years after it has become fashionable or accepted.

So if GetFoo returned a non-nil error, would the function abort and immediately return the error?

I'm used to a question mark being around null handling, but you know, JVM languages lol, null is thought about a lot.

Yeah exactly, it makes everything so much more ergonomic, especially with combinators, as in chaining method calls that might return an error
This has been used for ages, goddammit. The question mark idea kinda stops half way. In most of languages, the question mark is always there, implicitly. You have to add ugly code to say "this failure doesn't require bubbling up".

Go is an experiment in the exactly opposite direction: language puts pressure to really handle the error in a meaningful way every time. And if you bubble up, at least describe the situation a little bit better. Or explicitly give up and receive a penalty for that: the `return err` is nothing to be proud of, it's just a visual penalty for _lack_ of error handling.

I'm not saying it's better in every project, I'm saying it's valuable on some projects.

>There are a lot of programs where you don't need to think about errors

What kind of utopian programming job do you have that you don't have to think about errors? An error is not limited to technical issues like packet loss or unable to open socket. Its also "client A attempted to purchase item B which is limited to client C". How do you express this in Go?

I have no idea why people are so opposed to ADTs. Its like sliced bread with butter, or whatever the phrase is. Its not _that_ complicated...is it?

> What kind of utopian programming job do you have that you don't have to think about errors?

My job primarily requires thinking about errors. It is why I wish for Go-style errors in the languages I use.

But on rare occasions I write things like batch scripts, automations, etc. in which failure means addressing the issue in realtime and trying again. There you don't care much about errors other than ensuring that the world stops when an error occurs to allow you to fix the problem before continuing.

While contrived, if you run into a "client A attempted to purchase item B which is limited to client C" error in this space you're probably going to have to phone them up and tell them that you can't process the transaction, apologize for the mistake, remove record of their purchase, and then once complete run the script again. The program crashing with an error message is sufficient here.

Different tools for different jobs.

I like the reasoning behind Go's approach, but the `if err != nil` ends up polluting codebases like Java's checked exceptions did.

I'm not sure what the better way to do it is tbh.

Java's checked exception's could be handled at a single place by a catch, or just marked as `throws`. It was never as verbose as go is.
Maybe, but then, when Jackson is throwing an IOException because of bad JSON, and the file you opened that had the JSON could also throw an IOException, then it's not just catch it at a single place, as you'd generally handle both differently.
> but the `if err != nil` ends up polluting codebases

Pollute implies that it is unwanted, but this is something you do want. It is the most interesting and important part of your application logic. You want it up front and centre for all to see.

I think we all understand the human desire to want to believe that bad things won't happen, but when one becomes an engineer they have to set those emotions aside and realize that bad things will happen and that your job is to make sure that when bad things do happen that the failsafes achieve an acceptable outcome.

> I'm not sure what the better way to do it is tbh.

I'm not sure any better is fundamentally possible within an engineering context. The vast majority of the job is in understanding the failure modes and being able to communicate to the computer how to gracefully deal with the problems when they occur. As such, it stands to reason that the vast majority of the code is going to be related to errors.

If you are programming for hobby/learning purposes, where when bad things happen your program can simply crash with no consequence, there are different approaches that work well, but Go is decidedly not designed for this space. And, frankly, doesn't need to be as there are already plenty of languages designed for that. Go is, quite explicitly, meant to be an engineering language.

I can't agree with that at all. Checked exceptions can all be bubbled up to a single catch if that's how you want to handle it. That hardly pollutes a codebase.
Sure. But the bubbling of checked exceptions adds a lot of "foo() throws DeepUnderlyingException", all the way up to your single catch.

And the worst part is when reading the code in such a situation, it can get very tricky to figure out exactly where the exception is thrown.

There's a reason that the JVM ecosystem has largely moved away from checked exceptions.

This is pretty much exactly how I feel. I have great respect for the Go team. I broadly agree with their design philosophy. I think CSP is a great theory for concurrency. And I just don’t really like writing Go. I don’t hate it, but I feel like I have to either fight the language or just repeat myself an awful lot.

Honestly, I suspect the deficiency is on my end. Perhaps I’ve just spent too much time with Lisp and have experienced some professional deformation.

It's OK to have ergonomic opinions about languages! In scriptingland, I find Python abhorrent. Ruby fits my brain like a glove (the core language, not Rails). Doesn't indicate anything about the quality of the languages or their respective partisans.
Look at you guys using nice looking languages! Haha, Java got better, but oh god it’s still verbose.

We got rid of the getter setter silliness to be in “constructor land.” ( immutable records from Java 17 )

Function are still not first class citizens, but … they have a working visa now. You can shove lambda anywhere.

It’s still awkward to write functional code

Meh. Recently I’ve been really productive with it and I like the sheer boredom and lack of surprises.

Java is several times more expressive and less verbose than Go, objectively.
Most developers on the JVM with any freedom have switched to Kotlin.
Better check those numbers, only those forced by Mountain View minions have done so.
I'm talking about on the server, not Android... but sure. Bury your head in the sand.
I was free I would not develop corporate software. But oh well
citation needed
Any and all language trend survey results from the past couple of years.
I sorely wish CSP was enforced in any way by Go, but it's not. The only thing which arguably makes Go more "CSP-y" than, say, C++ is that it comes with a decent built-in concurrent queue and some syntax sugar. Goroutines have full mutable access to all the variables in their scope, and lots of Go code ends up being your good old mutex hell -- just more of it, since threads are so easy to create.

I think something way more radical than "C++ threads but with a built-in concurrent queue" is needed. If the default was to share nothing, and any shared memory had to be explicitly shared somehow, that would be a great step in the right direction. Maybe the compiler could even check that explicitly-shared variables are protected by a mutex; something like how Rust mutexes "own" the things they're protecting and you can't access the underlying object without having the lock.

Yes, that part might benefit from more constraints. There was a HN thread which approached it from a different angle: remove `go x()` and instead enforce mandatory use of WaitGroup/ErrGroup (i.e. a block that spawns them is the same block reaps them, always). I might extrapolate a bit here. https://news.ycombinator.com/item?id=26509986
I don't like _writing_ go, but I also don't hate reading & _debugging_ it.

I love writing lisp, but I hate reading & debugging lisp.

Lisp as practiced nowadays is mostly found in small teams with low turnover. As such, features of the language that necessarily have to scale with larger team sizes and turnover rates are probably just not that important to its current user base.

There's a Go code base that a friend and I first wrote 5 years ago and we incrementally update it as we find small bugs because of a system it interacts with that changes. We usually touch it once every quarter. I've had no trouble re-reading the code base and keeping it up-to-date. I can't say the same for a lot of other code bases I've worked in.

Yup, that's the tradeoff that most people somehow don't get: Go is maybe a bit more awkward and verbose to write than other languages, but it's much easier to read and refactor. My work with Go is (unfortunately) mostly private side projects which I sometimes have to leave for months, so I already learned to appreciate this years ago. Plus I also programmed in Delphi for years, so maybe some of the Pascal-inspired features of Go that programmers accustomed only to C-family languages find alien seem familiar to me.
> In the end I think if someone were to write a slightly simpler version of Rust with the Go runtime it might be pretty neat.

OCaml kind of gives you that, but the dev experience isn't as good as Rust or Go in my opinion. Still, I enjoy it a lot, more than Go or Rust.

Agreed - I really like OCaml. Unfortunately it isn't popular enough to get tons of ecosystem love, so I could never find the libraries I need. I'd be pretty happy if it got more popular. Maybe the new multicore work will help.
Yeah, came here to say the same thing. OCaml is pretty great, and the addition of multicore and algebraic effects will make it even better.

It does have some unique ecosystem elements such as Coq, WhyML, Mirage, etc. But common components could be definitely improved.

What about F#?
Rust is basically what you get if you start with OCaml, remove the GC, and add enough features to the type system to compensate for that without giving up on safety.
Does OCaml matches Go speed though ?
They actually sit right next to each other on programming language benchmarks game. Scroll down to CPU seconds, median and percentiles.

https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

The error handling was a disappointment at first, but in practice it is a lot clearer and more usable than any other kinds of error handling I've tried. It could perhaps do with some syntactic sugar, but it isn't a big issue since IDEs can pick up that slack. After about half a decade of Go I'd say that the error handling is something I've come to like.

"More expressive" in my opinion tends to mean "less readable", but there is some personal bias here. The people I've met during my career who like "more stuff" are usually the younger, less experienced programmers who aren't as concerned about long term viability.

I'm in my fifth decade of programming and my approach is "do more with less". When people want more stuff, for me, it means that I'll be dealing with more cases of people not knowing how all the stuff works. Everyone likes to think they can deal with more cognitive overhead, but mostly people can't.

I'm not sure if pattern matching belongs in a language like Go at all. Admittedly I haven't given it much thought, but it doesn't feel right for what Go aims to be. If you want to do Erlang, do Erlang, but I might be wrong about this.

What's error prone about the for statement? I don't use the C-style for statement all that often as way more than 50% of my for loops are ranging or naked for loops with break/continue/return.

Not having nil tends to lead to having to invent sentinel values all over the place, doesn't it? Is that really better? And since you often end up doing things by value in Go anyway, it isn't like you don't have any choice.

I used to dislike it too, but after being “forced” to use it daily (chose a job thats 100% golang on the backend), you really grow into it. It would be really, really nice to have better error handling and a bit less verbosity all around, but in terms of cost/benefit it still beats any other language I’ve worked with by a fair margin.

To get into it, it might help to start by modifying an existing project, a lot less painful than trying to write something from scratch without being familiar with the idioms.

> That isn't meant as a slight or anything other than simply my opinion.

That's fine, we all have our preferences. I love Python, but I can't stand Ruby. I love Go, but hate C++. I make my living with C#, but won't touch Java.

It's okay. We're not wrong. We're just different.

I agree. It feels a bit like where Java/JVM is at: popular, solid runtime, large community/ecosystem, but Java made some bad choices.

In my opinion: Go needs a Kotlin. First and foremost to do away with implicit nulls (imho the biggest mistake), but here are other things that could be impoved you've already mentioned.

The good thing is that Java is incorporating the proven features of other languages. It has gotten records, pattern matching (better than Kotlin's), and in the latest release, virtual threads with structured concurrency, better than async/await, and also better than golang as it lacks structured concurrency.
But implicit nulls are a big pain point (in both Java and Go). And its very hard to fix for Java (and no plant thus far).
There are solutions like CheckerFramework or Nullaway or other compile time annotation processors.
...and Kotlin :)
Better enums and non-nil types, that would be my wish list for Go. And conditional expressions, if there's one more wish to be granted. There's no need to make it resemble Rust: Go's type system is expressive enough as it is.

> has error prone C-style 'for'

Not really true. It has `for index, elt := range v` to iterate over arrays and maps. It's a pity you can't define your own range, that's true.

Custom ranges will be possible at some point: https://github.com/golang/go/discussions/54245
I totally agree. After working with it for a while I feel like its best use is in writing CLI tools due to the compiler making it so damn easy to produce statically linked binaries for any platform, backed by an incredibly powerful standard library.

My issue for application and web server development are in the language design as it restricts me in my personal quest to write loosely coupled code that is marinated in unit tests - at least when compared to other languages.

That said I enjoy the language, have learned a lot from it, use Go often (previous day job) and have a gopher plushy - but I am just not a die hard loyalist. There are things about the language that blow others out of the water, but there are significant portions that leave a lot to be desired.

What I have noticed is the language design has inconsistencies and there are lots of exceptions built into the language to get around missing features; features which are sometimes filled in later leaving ambiguity around approaches.

For example, Go uses a nominal type system with the famous exception of interfaces with methods that are structurally evaluated. The issue is interfaces only feature structural type evaluation on the top level, anything nested beyond that is nominal.

So an interface that has a method which returns an interface requires an implementing struct to return the _exact_ interface from that same method.

You can accept a struct where an interface is a parameter but you cannot return a struct where an interface is the return type.

This is useful when building type safe dependency containers, something that makes unit testing much easier.

Instead people just overuse `Context`, prop drill it into everything and cast types at some point when trying to get things from it.

Other examples are the return types from functions. Go didn't have generics initially so a Result[T] wasn't possible - but also exceptions were not possible. Tuples were not allowed by the type system but the language makes an exception for the function return types - fair enough.

Without type parameters and a need for generic basic types like `map`, `slice` and `sync.Map`, the language made an exception for the the primitive types - giving them generics however this is now inconsistent with the current implementation of generics.

Usage of `make()` and the confusion brought on by managing "references" also adds a bit of friction to the language.

Generics are inconsistent as well - for instance you can have a struct with a type parameter but not methods - however you can have functions with type parameters that accept a struct as their first parameter.

The tooling is a little underpowered, the test coverage analysis tool doesn't consider branch coverage, only statement coverage so you can have 100% test coverage reported with only 50% truly covered.

Making mocks requires cumbersome type generation - this is something I am okay with but the types generated have terrible assertion capabilities.

But there are phenomenal things from the language. Packages are a great design choice, module management from git is simple and effective, `range` is chefs kiss, the compiler is beautiful.

I love the idea of goroutines but I'm not blown away by channels. They are fancy iterators with dedicated keywords which I find can clutter things up a little - but they were really cool when Go first came out and we were only just starting to think about how to manage asynchronous concurrency.

"You can accept a struct where an interface is a parameter but you cannot return a struct where an interface is the return type."

Possibly I'm misunderstanding the complaint, but that is because an interface is a fat pointer, so one returns a pointer to a struct implementing the interface.

https://go.dev/play/p/YOa6NF7rjPF

It's more this scenario https://go.dev/play/p/S2yQqriYTtm

It's because an interface is structurally evaluated only on variable assignment, but it's not always structurally evaluated which limits its usefulness

Rust has set the bar so high that it’s going to be hard for other languages to justify their use.

If I had to make a bet, I’d say Typescript will take over for everything not performance critical and Rust will fit in every spot that either requires high quality assurance or high performance.

Rust users don't even register as a rounding error in the grand scheme of things. Don't get me wrong, I like Rust _the idea_. But to suggest Rust in it's current form, managed by it's current team, will be anything but a niche language for performance and safety critical corner cases is deluding yourself. The vast, vast, vast majority of programming to this day is still maintaining perfectly functional (but old) C++/Java/C/C# code bases. Anything overthrowing those thrones has to be both easy and better. Rust only fills one of these tasks. Go is far more popular because of the lower cognitive load, comparable speed, and relative ease at which you can become productive. Go won't replace Rust and comparing them is wrong. Go will, however, always be more popular than Rust. At least until Rust is adopted and supported by more major companies, used in major roles, and expands beyond it's niche.
Right, and no one will use Go, Java, Kotlin, Elixir, Ruby, Python, JavaScript, Haskell, OCaML, Ada, Lisp... anymore /s.
This truly is the final, futile iteration on the meme of: I want <current existing language> but with or without X:

> That said, I don't know what I'd want to drop from Rust so maybe I'm just fantasizing.

What is X? I don’t know.

Rust is adding new features at the speed of C++/C# which is quite bad imo, it's good a recipe to have different code base / way of doing things in just couple of years apart. Now for Rust there are many things that could be changed, async etc ...
At first I thought this too, but if you look at the actual "features" they are adding they are all more less just smoothing out existing features. There aren't really any major new ones I can't think of that expand the "surface area" of the language. There is just a lot of polish still needed, esp. to things like async, const, etc. and most of the features appearing are about reducing the burden to use existing features.
Exactly. All of the recent big features were added a few years ago, and now they're really just filling in the gaps.
What exactly has Rust added in recent months or even years? The way I see it most changes in the language are making existing features work more consistently.
V fixes most of these, but it doesn't use Go's runtime:

https://vlang.io/compare#go

I get you want to compare with Go, let have some benchmark. Despite V being new, the difference is quite insignificant for short-lived process in most case. You are more likely use Go for production at this time.

https://programming-language-benchmarks.vercel.app/v-vs-go

Depends on the use case and persons involved. Nevertheless, V is on a constant march towards 1.0, and has been doing so at a higher pace than most other languages.

And, its not necessarily an "either or" or "us versus them" situation, people can use both or whatever else "floats their boat". It's a bit disheartening to see programmers that fall into being so closed-minded or disparaging to other options or something new.

It also doesn’t work :)
You have never used it or are on an unnecessary disrespectful troll crusade. It works very well, for many of its fans, and keeps improving and getting better (at a high constant pace).

Watch, learn, download, and enjoy this V program (https://youtu.be/6H7dprSwr74), among so many other examples (https://github.com/vlang/V/tree/master/examples).