Live data from Hacker News

Why Go Can't Try

niketpatel.com

51–60 of 74 posts

Re: Why Go Can't Try

#51
post #30

The programmer is explicitly throwing away the error returned by ReadFile (using the underscore) in the criticism of Go. data, _ := os.ReadFile(path) Saying that is not explicit is just wrong.

I think the argument is that the compiler does not enforce that the error must be checked. It's just a convention. Because you know Go, you know it's convention for the second return value to be an error. But if you don't know Go, it's just an underscore. In a language like Rust, if the return type is `Result `, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an erro…

Go has tools for checking things like this. It's just not in the compiler. If you don't want to enforce that all errors are checked, go doesn't force you to. If you do, it requires you to run an extra tool in your build process.

(Or in your commit hook. If you want to develop without worrying about such things, and then clean it up before checkin, that's a development approach that go is perfectly fine with.)

Re: Why Go Can't Try

#52

The programmer is explicitly throwing away the error returned by ReadFile (using the underscore) in the criticism of Go. data, _ := os.ReadFile(path) Saying that is not explicit is just wrong.

Criticisms of Go seem like they pivot on the author's understanding of what's being done with `_` and sometimes `nil`. It's a strongly-typed language with a lot of flexibility around type, and that's nice to have when working on edge systems like a data ingester.

Re: Why Go Can't Try

#53
post #30

Earlier quoted context omitted.

I think the argument is that the compiler does not enforce that the error must be checked. It's just a convention. Because you know Go, you know it's convention for the second return value to be an error. But if you don't know Go, it's just an underscore. In a language like Rust, if the return type is `Result `, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an erro…

When you see .unwrap in Rust code, you know it smells bad. When you see x, _ := in Go code, you know it smells bad. > But if you don't know Go, it's just an underscore. And if you don't know rust, .unwrap is just a getter method.

One big difference is that with unwrap in Rust, if there is an error, your program will panic. Whereas in Go if you use the data without checking the err, your program will miss the error and will use garbage data. Fail fast vs fail silently.

But I'm just explaining the argument as I understand it to the commenter who asked. I'm not saying it is right. They have tradeoffs and perhaps you prefer Go's tradeoffs.

Re: Why Go Can't Try

#54
post #30

Earlier quoted context omitted.

I think the argument is that the compiler does not enforce that the error must be checked. It's just a convention. Because you know Go, you know it's convention for the second return value to be an error. But if you don't know Go, it's just an underscore. In a language like Rust, if the return type is `Result `, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an erro…

When you see .unwrap in Rust code, you know it smells bad. When you see x, _ := in Go code, you know it smells bad. > But if you don't know Go, it's just an underscore. And if you don't know rust, .unwrap is just a getter method.

> When you see x, _ := in Go code, you know it smells bad.

What if it’s a function that returns the coordinates of a vector and you don’t care about the y coordinate?

Re: Why Go Can't Try

#55
post #5

The author doesn't touch on it, but the bigger problem with things like Foo|Bar as an actual type (rather than as a type constraint) is that every type must have a default/zero value in Go. This has proven to be a difficult problem for all of the proposals around adding sum types, whether they're used as errors or otherwise. For example, to support interface{Foo|Bar} as a reified type, you'd have to tolerate the nil…

Why not just define default(Foo|Bar) as default(Foo)?

Re: Why Go Can't Try

#56

The Go team has discussed syntactic sugar for error handling many times. They're not against it, they're just holding out for a proposal that checks a lot of boxes and makes everyone happy, which hasn't happened yet.

> and makes everyone happy

But that's never going to happen because there is a very vocal subpopulation (I think it is a minority, but don't know for sure, and it is hard to measure that kind of thing) that is opposed to any "syntactic sugar" and wants it to stay the way it is.

Re: Why Go Can't Try

#57
post #55
post #5

The author doesn't touch on it, but the bigger problem with things like Foo|Bar as an actual type (rather than as a type constraint) is that every type must have a default/zero value in Go. This has proven to be a difficult problem for all of the proposals around adding sum types, whether they're used as errors or otherwise. For example, to support interface{Foo|Bar} as a reified type, you'd have to tolerate the nil…

Why not just define default(Foo|Bar) as default(Foo)?

There are cases where privileging the first alternative makes sense (like Maybe[T] = None|T with None = struct{} e.g.), and there are cases where it doesn't (like Either[L,R] = L|R). There has been an extensive amount of discussion about a number of different proposals, e.g. https://github.com/golang/go/issues/54685, https://github.com/golang/go/issues/57644, etc., but so far, much like "better" error handling, no workable consensus has yet emerged.

Re: Why Go Can't Try

#58
post #35
post #30

Earlier quoted context omitted.

I think the argument is that the compiler does not enforce that the error must be checked. It's just a convention. Because you know Go, you know it's convention for the second return value to be an error. But if you don't know Go, it's just an underscore. In a language like Rust, if the return type is `Result `, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an erro…

> if the return type is `Result `, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an error (match, if let, unwrap etc.) I think you can make the same argument here - rust provides unwrap and if you don’t know go, that’s just how you get the value out of the Result Type.

The big difference is that with `(T, error)` as a return type, any value on the caller side will look like a valid one (thanks to zero values).

  a, err := f()
  // whether you forgot to handle the `err` or not, 
  // the `a` carries a zero value, or some other value.
In rust it's not the case, as the `T` in `Result` won't be constructed in case of an error.

Re: Why Go Can't Try

#59
post #30

Earlier quoted context omitted.

I think the argument is that the compiler does not enforce that the error must be checked. It's just a convention. Because you know Go, you know it's convention for the second return value to be an error. But if you don't know Go, it's just an underscore. In a language like Rust, if the return type is `Result `, the caller cannot access the `MyDataType` without using some code that acknowledges there might be an erro…

Go has tools for checking things like this. It's just not in the compiler. If you don't want to enforce that all errors are checked, go doesn't force you to. If you do , it requires you to run an extra tool in your build process. (Or in your commit hook. If you want to develop without worrying about such things, and then clean it up before checkin, that's a development approach that go is perfectly fine with.)

> requires you to run an extra tool

And the more I work with Go, the less I understand why warnings were not added to the compiler. Essentially instead of having them in the compiler itself, one needs to run a tool, which will have much smaller user base.

But anyway, in Go, it's sometimes fine to have both non-nil error and a result, e.g. the notorious EOF error.

Re: Why Go Can't Try

#60
post #23

In Zig you need an allocator to allocate anything, so whenever you need to add some extra information to an error, you pass a diagnostics object as an output argument to a potentially failing function. In this case it becomes a bit harder to compare it to Go's errors, each with pros and cons. I think comparing Go errors to Rust errors would be more fair. There are some articles about the diagnostic pattern in Zig, e.…

How so? In Rust you also need an allocator to allocate anything. Zig's diagnostics idiom is just that, an idiom. It would be very weird to do this in Rust, but then it's a pretty weird choice in Zig, they've just decided to do it anyway.

What I meant by "you need an allocator" is "you need to explicitly pass the allocator of your choice to the function" [if you want to attach some data to the error]. In rust you can simply return anything as error, not only an enum variant.
Post reply on HN