Live data from Hacker News

Ten years of “Go: The good, the bad, and the meh”

blog.carlmjohnson.net

61–70 of 305 posts

Re: Ten years of “Go: The good, the bad, and the meh”

#61

Earlier quoted context omitted.

a, err := f() if err != nil { return } a, err = f() This will compile.

I'm not at all defending the practice. I agree it's very easy to navigate around this. My answer is just the canonical one I've seen over and over again in books about Go.

It's not just that it's "very easy to navigate around this", it's that it's not true. The incorrect statement piggybacks on Go forbidding unused variables, however that has two absolutely major holes:

First, it requires having a variable in the first place, if you call a function for its side-effect and don't remember that it returns an error, Go won't tell you.

Second, Go only errors on dead variables, not dead stores, since conventionally the error variable is "err" you can easily forget to check one of them, and Go won't say anything because the err variable was read in one of the other checks.

It's even worse if you're one of the weirdoes which uses named return variables, because named return variables are always used:

    func foo() (v int, err error) {
        a, err := bar()
        b, err := baz()
        v = a + b
        return
    }
compiles just fine. But at least you're returning the second error. No such luck if you're using named return variables for documentation:

    func foo() (v int, err error) {
        a, err := bar()
        b, err := baz()

        return a + b, nil
    }
go also has nothing to say about this.

Re: Ten years of “Go: The good, the bad, and the meh”

#62
post #57
post #46

> Again, it shows how things have changed that I praised Go’s type inference as an advance in the state of the art, but now the Hacker News crowd considers Go’s type inference to be very limited compared to other languages. "You either die a language nobody uses or live long enough to be one people complain about." This rubs me the wrong way. Even back when Go first came out, anyone who knew anything about programmin…

> anyone who knew anything about programming languages rolled their eyes at pretty much everything about Go's type system And Go has succeeded despite these condescending diatribes on how a language needs to have a Hindley-Milner type system with ADTs and type classes to be useful. Go made me truly realize how insufferable the PLT community is, and why they are so absolutely lost when it comes to creating successful…

Go really is great. There was a big argument around a new project we were starting whether to do Go or Clojure.

What we did to settle the debate was to ask an entry level dev that was just starting if he was interesting in writing two sample applications in each language, having known nothing of either. Nothing complicated but touched enough points (HTTP endpoints, database interaction)

A full day later was still trying to get the Clojure app working correctly.

He finished up the Go one in like an hour.

Since then we've brought new devs on with zero Go experience and they are up writing good code in a day. I can't imagine where we would be if we had gone down the Clojure route.

Re: Ten years of “Go: The good, the bad, and the meh”

#63
post #59
post #30

Earlier quoted context omitted.

a isn't used so it won't ;p Honestly the thing that I think lacks more is macros. With macros it would be trivial to write a := Must!(f()) that * assigns last return value to err * calls return with that error if it is not nil

b, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read %s: %w", path, err) } is so much better than b := Must!(os.ReadFile(path)) because when things go wrong, I have exactly the right amount of information I want. Assigning to err magically (it's not even mentioned in the source code) is exactly the kind of thing that'll turn out to be the cause of a subtle bug 6 months later. Why not spend the a c…

> I have exactly the right amount of information I want.

Really? Because `ReadFile` already adds the path to the context, so what you actually get is

    read /tmp/foo: open /tmp/foo: no such file or directory
which is more confusing than "the right amount of information".

Furthermore nothing precludes `Must!` taking a prefix and wrapping automatically, does it?

> Assigning to err magically (it's not even mentioned in the source code) is exactly the kind of thing that'll turn out to be the cause of a subtle bug 6 months later.

What bug? It's assigning and returning, the only situation where you'd have "a subtle bug" is if you didn't check the previous call and overwrote its error, which is exactly what you get with the code you propose.

Re: Ten years of “Go: The good, the bad, and the meh”

#64

Earlier quoted context omitted.

What's the tradeoff? what advantage having null pointers give?

default values for everything without significantly increasing language complexity

As far as I'm concerned, that's a drawback. Ubiquitous default values are an attractive nuisance. One which C# had already demonstrated 10 years prior.

The removal of nil leading to the removal of ubiquitous default values would have been positive.

Re: Ten years of “Go: The good, the bad, and the meh”

#65
I'd categorize not being able to convert "unused thing" errors into warnings during development iterations as one of "The Bad".

Just today I had a couple blocks of code which were causing erratic issues. Wanted to see if it was the second one, so I quickly commented it out. This is just an exploratory development session, no need to comply with code quality guidelines. Still, the code failed to compile because now I had to worry about 8 lines with stuff that had become unused. The stubbornness about not adding an escape hatch for, again, exploratory intermediate development iterations is unnerving.

"But code should not leave unused stuff around". I agree. That's why after a hundred iterations, in the final compilation phase for production, this kind of errors-are-warnings flag would be disabled.

Re: Ten years of “Go: The good, the bad, and the meh”

#66

Earlier quoted context omitted.

default values for everything without significantly increasing language complexity

As far as I'm concerned, that's a drawback. Ubiquitous default values are an attractive nuisance. One which C# had already demonstrated 10 years prior. The removal of nil leading to the removal of ubiquitous default values would have been positive.

I think that's why people in this thread are calling it a tradeoff. It's a very attractive option that seems like a great idea until it breaks. In happy path having default values is better, in mixed path situations, having a separate and reserved way of saying something hasn't been touched is incredibly powerful

Re: Ten years of “Go: The good, the bad, and the meh”

#67
post #65

I'd categorize not being able to convert "unused thing" errors into warnings during development iterations as one of "The Bad". Just today I had a couple blocks of code which were causing erratic issues. Wanted to see if it was the second one, so I quickly commented it out. This is just an exploratory development session, no need to comply with code quality guidelines. Still, the code failed to compile because now I…

I use a technique I call "runtime comments":

  if false {
    ... stuff I don't want to run right now but the compiler still has to deal with it ...
  }

Re: Ten years of “Go: The good, the bad, and the meh”

#68
post #42

Go didn't try to be overly clever and abstract. That rubs quite a few people the wrong way, but it also means you are less likely to have to work with people who try to be clever. My first reaction when seeing Go is that it smelled of "old fart". It looked straightforward and unexciting. I'm an old fart. I like straightforward and unexciting. It tends to lead to code that I can still read 6 months from now. I want to…

Yeah this was my primary complaint with the inclusion of generics. People will try to be all clever and their code will wind up as an unreadable, unmaintainable disaster. It has definitely led to some cool stuff, but I prefer boring, verbose, and clear any day.

Problems are generic, such as having a tree collection. For solving generic problems, you can either use language generics, or reflection, or type erasure, or codegen. The latter three are about as far from 'boring and clear' as you can get. Still verbose, though, but I don't expect that's a benefit.

Re: Ten years of “Go: The good, the bad, and the meh”

#69

Earlier quoted context omitted.

Dynamic typing via interface{} is also huge Go code smell though, so... Generics are definitely better for you. But I would say the overall pattern you're employing of bulk inserting different kinds of data structures with one function is the problem. Of course, I don't know your code so I'm sure you have a good reason for choosing what you did, but a BulkInsert of Any certainly made me raise my eyebrow.

The reason is the same reason that anyone ever writes a generic function (outside of writing a library) - to keep code DRY and avoid duplication. There is no upside to maintaining a large number of identical function implementations, and no scenario in which that is preferable to using generics.

But you probably will have to differentiate at some point between what you're inserting. Why not just do that, instead of artificially combine it into one function?

Nevertheless I feel like I'm getting lost in the weeds of this example. Obviously DRY and less duplication is good. However, you have to strike a balance between being clever and being clear. And frankly I would prefer 3 lines of clear code to 1 line of hard-to-read code.

Re: Ten years of “Go: The good, the bad, and the meh”

#70
post #57
post #46

> Again, it shows how things have changed that I praised Go’s type inference as an advance in the state of the art, but now the Hacker News crowd considers Go’s type inference to be very limited compared to other languages. "You either die a language nobody uses or live long enough to be one people complain about." This rubs me the wrong way. Even back when Go first came out, anyone who knew anything about programmin…

> anyone who knew anything about programming languages rolled their eyes at pretty much everything about Go's type system And Go has succeeded despite these condescending diatribes on how a language needs to have a Hindley-Milner type system with ADTs and type classes to be useful. Go made me truly realize how insufferable the PLT community is, and why they are so absolutely lost when it comes to creating successful…

golang mainly ended up competing with and replacing the likes of python and ruby, where it was intended to compete with C and C++, where it didn't really change anything.

It makes sense in retrospect of course, python and ruby are slow, dynamically typed languages, and any improvement in performance and typing is welcome. It doesn't mean that golang is inherently better somehow to other offerings. I still maintain that Java and C# are superior languages and ecosystems.

Post reply on HN