Live data from Hacker News

Declined Proposal: A built-in Go error check function, “try”

github.com

341–350 of 425 posts

Re: Declined Proposal: A built-in Go error check function, “try”

#341

Earlier quoted context omitted.

I thought it could lead to doing method chaining for a fluent like API which I find cleaner than how things work now.

I really dislike method chaining. I'd much rather have 5 lines than 5 chained methods. If that's too much to read, you can always encapsulate it in a well-named function.

But five functions that return a value and an error would each have to run the if err != nil dance whereas with method chaining it's cleaner

Re: Declined Proposal: A built-in Go error check function, “try”

#342

Earlier quoted context omitted.

You can't compare it to Java checked exceptions. Java checked exceptions are one of those billion dollar "mistakes". It is the single worst thing in Java for me. Checked exceptions add nothing and break almost everything around clean design & code. For that comparison to work, java would have needed to only have checked exceptions, with some added language features to deal with them cleanly, and then it might actuall…

> Littering your code with try-catch is what makes exceptions infeasible for error handling. I honestly don't see the big difference between littering your code with if/else blocks versus littering them with try/catch blocks. Can you elaborate?

If/else provides a clear and easy to follow control flow. try/catch is like a roaming goto that works it's way back up your stack in ways you can't predict.

http://www.lighterra.com/papers/exceptionsharmful/

Re: Declined Proposal: A built-in Go error check function, “try”

#343
post #213

Earlier quoted context omitted.

> it has means that there are not many that can compare in its niche Which is? "devops"? Otherwise, offerings on the JVM and .NET are strictly superior. And now with native compilation being available for both platforms, they will also be a better fit at devops than golang. Other alternatives include Rust.

I would say the niche is basically clouds systems programming and general server side apps. JVM based language can't compare due to the JVM. The last thing you want for that type of programming is a huge, complex virtual machine to worry about. That plus memory issues, slow compiles, complex deployments makes it not really in the running. The .net based languages are a bit more interesting, but they have a _lot_ of g…

> The last thing you want for that type of programming is a huge, complex virtual machine to worry about. That plus memory issues, slow compiles, complex deployments makes it not really in the running.

Sorry, but I don't buy this at all. golang also ships with a runtime, that's why binaries are dozens of MBs.

The JVM is very configurable, which is what I assume you mean when you say "complex". This configurability is what allows it to be tuned to the task at hand. You can cap the maximum memory it's allowed to use, try out different GCs based on your use cases, etc.

Compilation times are not slow the least bit, especially with incremental compilation. Incidentally, for the majority of the time, it takes way less time from when I press the run button for a Java/Kotlin project in the IDE to having it up and running, compared to projects I worked on in golang, where the entire code base has to be rebuilt.

People keep repeating things like "memory issues" or "complex deployments". For a long time now, people have been shipping self-contained "fat" or "uber" jars. Running them is as simple as `java -jar program.jar`. And since the JVM defaults to G1, memory usage is generally less - the JVM is tuned for performance at the expense of memory saving, but that can obviously be changed, even more so recently with GraalVM[1]. Java has already been used on embedded systems (including card readers) - even Spring that has a reputation for being "bloated" can run on a Raspberry Pi.

[1] https://quarkus.io/

Re: Declined Proposal: A built-in Go error check function, “try”

#344

Earlier quoted context omitted.

> Being able to express that a bunch of internal exceptions should be wrapped in an application exception would save a ton of boilerplate. It would but I think it's the wrong approach -- you're actively changing the error information that really provides no additional value except to make the type-checker happy because the alternative is too verbose.

Doesn’t wrapping usually mean adding (semantic) information without changing the original?

You no longer propagating the same error; if you were type-checking on network exceptions, for example, you would entirely miss it if it was wrapped in LibraryException instead.

Wrapping does have value if you are really adding useful information but the example here is just changing the type which really doesn't add anything useful.

Re: Declined Proposal: A built-in Go error check function, “try”

#345
Quite rightly, too! It's annoying for beginners, but you quickly see the utility in the if err != nil {} approach, or 'sad path' approach after a few years of using Go in production. You learn to love it! To be honest, there's very little I'd add to the Go language, if anything. It's very unique that most Go developers share that view of a language. With Javascript, for example, I can't wait for 'new stuff', I think that's sometimes symptomatic of a broader dissatisfaction.

Re: Declined Proposal: A built-in Go error check function, “try”

#346

Earlier quoted context omitted.

I understand what you mean by “bad ergonomics”, but I think of those things as “ergonomics in the small”. You end up writing for loops and error checks. It’s verbose but not complex, and it’s all very localized. Further, people get really hung up on these small language issues and miss go’s killer features: simplicity and consistency. Go is a small, simple language with few surprises. No guesswork about which feature…

Go the language is surprisingly complex and error prone compared to other GC languages because of its decision to allow explicit references. This adds an extra layer of semantics to nearly every aspect of the language. For example, the semantics of the for-over-collection statement in Java are that it repeats the block of code, with the loop variable holding each value of the collection in turn. In Go, the equivalent…

You largely missed the point of my post. I was showing that Go values simplicity and consistency. The examples I used to illustrate that were exactly that: examples. They were not a list of features that were novel to Go, as you seem to have interpreted.

> Go the language is surprisingly complex and error prone compared to other GC languages because of its decision to allow explicit references.

It's true that having value types in addition to reference types (while other GC languages often only have reference types) adds some complexity to the language, it's not much and it's still much less complex and error prone than other GC languages. Also, C# has value types and Java desperately wants them, so I think it's pretty clear that they're worth the extra bit of complexity.

> This adds an extra layer of semantics to nearly every aspect of the language.

I don't think this is meaningfully true. You have to think about whether a thing is a value or a reference type. This is exactly one bit of additional complexity.

> Slices and arrays are another complicated area of the language, with gotchas like append sometimes modifying the original array, sometimes not.

Slices are actually quite simple, but people run into issues because they expect them to behave exactly like Python lists or JavaScript arrays. Slices are views into an underlying array, and appending to a slice always modifies the backing array; however, if the append causes a grow, then the backing array is now a different array than the original. Of course, your point stands in that this difference can be frustrating and that frustration is a real cost--but you run into that cost once or twice and you update your understanding and rarely encounter it again.

> Regarding splits, you still have that in Go as well - do you use goroutines as coroutines, sending copies of objects through channels? Or do you use them as threads, with shared memory and locking? Do you use the testing package as is, with its lack of any user-friendly asserts? Or do you pick up an assertion library? Do you use raw http,or something la Gorilla? Sql or some ORM? Do you log to stdout, or do you pick up a logging library?

I never claimed Go makes every decision for you, only that there are fewer decisions and the happy path is more obvious. You always use goroutines as threads and whether you use channels or locks is a design question (different use cases). The standard testing library is the happy path. You can add on an assert library if you really need it (although you probably don't). Similarly you use the raw HTTP library until you really need something more (you probably don't). Stdlib database/sql package or ORM? Again, database/sql until you need an ORM (again, you probably don't). Standard library logging package or a logging library? Again, you use the stdlib (happy path) until your requirements outgrow it. Notice the pattern?

> Regarding gofmt, I personally can't understand the passion some people have for enforcing a common style.

It avoids wasting time in nitpicky style conversations in code review and makes things easier to read. You're welcome to your opinion, but that's the rationale.

> Regarding build tools, Go is hardly unique among modern languages in having built-in tooling for that.

I didn't claim otherwise, only that Go's standard build tool is yet another example of simplicity and consistency. And many languages don't have a standard built-in tool, and the ones that do are often complex. The only language with a nicer build tool IMO is Rust, and like everything Rust vs Go, the Rust build tool prefers pragmatic complexity to simplicity (a philosophical difference that I can respect).

> Even then, if you have any other build artifacts you may find you need to reach for something other than Go's tooling. By comparison, Maven can easily handle a Java+minor bits-in-other-languages project out-of-the-box.

I don't see why every language should have a build tool that can build it + small bits of other languages. A build tool should build that language well, and extend it for bigger projects using a wrapper tool like Make or Bazel depending on use case. Unix philosophy and all that.

> Not to mention that Go is probably the only language in any kind of popular use today that doesn't have a built-in way to interface with C code (you need to use a separate compiler if you want that!).

Every language (even C++) needs another compiler to compile C code before it can be called into. And once it's compiled, it's no more difficult to call into it from Go than from Java or C# or Python or etc. It's also often easier, since you can use C values directly in Go without needing to write shims (e.g., PyObject shims). That said, like all GC languages, it's fundamentally hard to correctly manage ownership for references across the C/$LANG boundary.

Re: Declined Proposal: A built-in Go error check function, “try”

#347

Earlier quoted context omitted.

Seems identical to Java: try { var val1 = happy_path1(val0); var val2 = happy_path2(val0, val1); var val3 = happy_path3(some_val); function_might_crash_let_it_crash!(some_val); return happy_result(); } catch (NotFoundError e) { handle_notfound_error(); } catch (PermissionError e) { report_permission_error(); } catch (Exception e) { throw new Exception("don't worry this process is supervised, let it crash!"); }

If you're rethrowing the exception, what happens in the VM if that exception is not caught? IIRC, the VM exits, so, it's not at all identical. There are potentially severe nonlocal effects and you're already coding defensively by putting a catch/rethrow.

As far as I know, you didn't get a supervisor for free in Elixir either, you decided to use one, and spent some time determining how to configure it to recover from its child processes failing. In Java, you can similarly put a try/catch at a higher level in the call tree and decide to retry everything on error. Now, Erlang processes don't share state, so that makes them easier to "handle" when they crash, I'm not denying that. My point was more that "try" can be as ergonomic as "with".

The re-throw was just to mimic your Elixir example. Normally I'd just let exceptions that shouldn't be handled at that level bubble up the call tree to wherever it makes more sense for it to be handled.

Now, I'll admit, most Java code I've seen professionally use exceptions badly, in that they tend to be overly aggressive and defensive, try-catching everywhere at all levels when they should just let things bubble up as appropriate. I believe that's just a general misunderstanding though. Often it is caused by Java's use of CheckedExceptions, programmers just try/catch to make the compiler error go away, when they should just re-declare the exception since it doesn't need handling most of the time, or re-throw a runtime error if they don't care to have compile time checks for handling them.

When I code in Java, I barely ever try/catch anything. Normally I have one try/catch around my main method which can recover the whole app from any error, and from there, if there are smaller process trees that can be recovered independently at their level I sprinkle a few more try/catch in those places.

P.S.: I also don't want to get into too many details, but the VM actually doesn't crash, Java will call your UncaughtExceptionHandler for any given thread that crashes, the default one performs a System/exit in error state, but for a server app for example, you'd handle it.

Re: Declined Proposal: A built-in Go error check function, “try”

#348
post #214

Earlier quoted context omitted.

I understand what you mean by “bad ergonomics”, but I think of those things as “ergonomics in the small”. You end up writing for loops and error checks. It’s verbose but not complex, and it’s all very localized. Further, people get really hung up on these small language issues and miss go’s killer features: simplicity and consistency. Go is a small, simple language with few surprises. No guesswork about which feature…

golang's "simplicity" (i.e. unexpressivity and weak modeling ability) translate into complexity in real world code bases. There's no way around it.

Those things do translate into some complexity in some real world code bases, but other features save much more complexity. On balance, Go comes out ahead IMO.

Re: Declined Proposal: A built-in Go error check function, “try”

#350

Quite rightly, too! It's annoying for beginners, but you quickly see the utility in the if err != nil {} approach, or 'sad path' approach after a few years of using Go in production. You learn to love it! To be honest, there's very little I'd add to the Go language, if anything. It's very unique that most Go developers share that view of a language. With Javascript, for example, I can't wait for 'new stuff', I think…

Agreed. When I first started writing go I thought "Wtf is this caveman language"

And now years later I couldn't imagine going back to using exceptions. The non-linearity of exceptions creates such a cognitive load of picking up unfamiliar code, and this is where go really shines. I can dive into almost any go codebase and get a lay of the land very quickly.

On another note, I've been enjoying rust's approach with Result / the ? operator. Same principle with less visual bloat.

Post reply on HN