Hacker News new | ask | show | jobs
by cube2222 1652 days ago
Agreed.

I wrote a bunch of Rust, Scala, Haskell, but I still greatly prefer Go, even without generics.

I am very happy with the generic container libraries I'll get with generics, but I hope people won't try to be too clever (as they usually do). So far, Go is a language that just rules out a lot of bikeshedding, which I very much appreciate. We'll see how that evolves.

I do think premature or wrong abstractions are a much bigger and widespread problem than a lack of abstraction.

Also, I really like Go's error handling, it results in error messages in Go projects usually being top-notch (because of explicit handling and wrapping which includes relevant context and human-readable messages).

3 comments

I'm with you. Go has its problems, and I am aware of them, but it is just so compatible with the way my brain works, it is amazing.

I remember listening to a podcast about C++, and the guest explained how after working with C++ for about five years, they still encountered aspects of the language that surprised them on a regular basis (to be fair, though, that was before C++11). To me, Go just clicks in way few languages did.

I think language preference is very much about how people’s brains work (and they all work differently!). My brain struggles with Go, but clicks with Rust.
This must be it and ditto for me on Rust. It feels like they made the language just for me.
Interesting - I've also written a ton of code in those languages (not as much in Haskell), and Go would be my last choice by a large margin (and Rust my favorite by a wide margin). I went through a love/hate relationship for a while (it lets me create static executable, compiles pretty fast, it is easy to remember, etc. but if I have to type "if err == nil" one more time!!!).

While I applaud the focus on simplicity, I found it simply transfers that burden to the programmer (I have to loop over a map to clear it...really?). Every single "lack of" feature in Go (has nil, no sum type errors, no pattern matching, etc.) is in Rust which gives me endless freedom to express safe, correct programs. I suppose language choice is highly individual, but it still perplexes me as to what people see in Go over Rust.

I'm sure everyone's experiences differ, but in my travels Go has shown to carry the lowest rate of "who wrote this shit?" when you come back to a project five years later. I agree that it puts the burden onto the programmer upfront, but reduces it down the road. Tradeoffs...

I have a lot more fun writing code in other languages. I enjoy not having that burden on me while writing code. In an earlier life that would have been important to me. Now that I'm old and curmudgeony I've started to value other things.

I work in a team, where not all are unicorn Rust specialists. The work we do doesn't require millisecond-level response times, nor is it something that needs to be ultra-safe in relation to memory safety.

I can teach any mook with basic Java/C# programming knowledge how to be productive in Go in less than a week. At this point they can read pretty much any Go code pretty fluently and can be trusted not to commit anything stupid.

Can you say the same about Rust?

> I work in a team, where not all are unicorn Rust specialists

> I can teach any mook with basic Java/C# programming knowledge how to be productive in Go in less than a week.

This is fair, and probably the reason why Go continues to be popular I guess

> The work we do doesn't require millisecond-level response times

Rust is a high level language, and it is a bit of a misnomer that it is only good for low level things. Most of my stuff doesn't require this level of speed either (the previous major version of my project was written in _Python_). I use Rust for the safety and data structure benefits, not speed.

> can be trusted not to commit anything stupid

As a Rust coder, and a fan of functional programming as well, I personally find any "null pointer" error quite "stupid" and unnecessary as is the occasional "err == nil" instead of "err != nil", or forgetting to check it at all. We will probably have to disagree on what constitutes stupidity, and that is fine.

Absolutely. You can write some Rust by following some hello world tutorial and be productive in 15 minutes. That doesn't mean you'd be proficient in writing procedural macros or understanding all details of memory management, but I've seen Linux magazines presenting Rust omitting all those "advanced" features and surprising amount of Rust users is new to programming.

The learning curve is usually different because you will need to understand more before program will compile and because most materials aims to cover 100% of the language from day one, but if you want to approach it differently, productivity wouldn't be a problem.

Just so we're clear, you're saying that Rust only takes 15 minutes to learn and be productive in, for the average person?
Basic Rust, sure. For some definition of "basic", and average person that knows some other programming language. What I mean is something along this line:

https://www.linuxjournal.com/content/getting-started-rust-wo...

It leaves many concepts without full explanation, but that's not necessary to do something useful.

Premature or wrong anything is clearly bad. But is parameterisation a premature or wrong abstraction? I'd say that parameterisation - even in the type language - is the original proven abstraction.
I think this is why I always have trouble understanding arguments against generics (in the general case at least). Parameters are added to functions/methods in order to make them more generic/flexible, as a rule. To move something that may have been hardcoded into something that can now be configured:

  get_data() { filename=default ... }
  =becomes=>
  get_data(filename=default) { ... }
When the type either does not matter, but the concrete instance records it, or the type makes sense to be configurable, you want generics. As a silly example (but short enough to fit into a comment block):

  fn nth(seq: [int], n: int) -> int
Now you have to make a new nth for every single sequence type, even though it has no bearings on the actual operation. Or you make it generic:

  fn nth<T>(seq: [T], n: int) -> T
That's a trivia case, yes. But if anyone has ever worked on a complex code base there are plenty of situations like this that turn up, at least in my experience. Sure, I almost always start off with concrete instances with a fixed type, but as soon as it becomes apparent that the type itself is irrelevant and I have a couple use-cases with different types, why not make it generic and be done with it? Like, would you really have more than one version of that get_data function running around, one for every conceivable filename? That would be obscene. Why would you do the same with types?
Yes, in my opinion oftentimes it is.

There is a reason why even in mathematics people like to operate on concrete examples to get an intuition. For many, concrete is much easier to understand than abstract.

That's the less important point. The more important point is that making your code generic often involves more trickery which makes the code more complex, even if you only use the code once or twice - so that's just effort wasted.

The fact that parameterization is a proven abstraction doesn't mean it's good everywhere. Same as I don't agree with the "Clean Code" way of creating a myriad of 4 line functions.

Yes, good programmers won't make these mistakes, you can totally handle them. But when arriving at legacy code or open-source projects I greatly prefer to find under-abstraction rather than over-abstraction.

To be clear, I'm not against generics, I just agree with the parent of my original message. I'm worried people will overuse them and I don't want a whole laundry list of Rust features in Go. I'm very happy about libraries with type-safe generic B-Trees.

I get what you mean, and I personally rarely write generics, but when I need them they're great. I think that a good rule of thumb that would be easy to implement and review would be "no generics outside of libraries". This way, you get your type-safe containers, your application code doesn't get much more complex than before, and if you want to introduce generics, you have to really think about it.