16 ms·
This is fantastic. It'll make Go feel much less weird. eg from the diff: []interface{}{1, 2.0, "hi"} -> []any{1, 2.0, "hi"} Now that Go is going to have gen
by kbd 5y ago
This is fantastic. It'll make Go feel much less weird. eg from the diff:
[]interface{}{1, 2.0, "hi"} -> []any{1, 2.0, "hi"}
Now that Go is going to have generics, all we need is sugar syntax for early return on error -- like more modern languages such as Rust and Zig have -- and Go may finally be pleasant to program in!
- linkdd 5y agoI cannot wait for the Result monad. The state of error handling in Go at the moment is embarrassing at best.
- papito 5y agoPeople downvote it, but it's true. 2/3 of your Go code is `if err` blocks. And the way you have to chain the error messages to make the stack make any kind of sense is just maddening.
- Vanclief 5y agoI used to really hate that. After reading this amazing post https://middlemost.com/failure-is-your-domain https://middlemost.com/failure-is-your-domain I created a module that makes this bearable https://github.com/Vanclief/ez https://github.com/Vanclief/ez
- neop1x 5y agoWe often have errors handled. In contrast to many Java codes just spitting unhandled exception stacktraces or crashing completely.
- papito 5y agoYou must handle an exception in a language like Java. Yes, you can just print the stacktrace, but that is a choice. I don't HAVE to handle errors in Go until the program crashes as well. I never really understood that argument.
- tptacek 5y agoA lot of people are going to write very un-idiomatic Go code with "Result" types built on simple generics that, 5 years from now, we'll all be kind of smirking at. Rust's Option and Result make sense because the language supports it (most importantly with match statements). They don't make sense here.
- linkdd 5y agoFun observation of mine: most articles/tutorials I've read about "idiomatic go code" are presenting ideas that you never see in any production code. Seems to me that the ones who write about "idiomatic go code" aren't the ones who are shipping softwares/libraries.
- nojito 5y agoThis is true of all developer evangelism in general.
- tptacek 5y agoThis seems pretty glib. There's pretty obviously a phenomenon where people try (or used to try) to write code for other languages (less commonly Rust, more commonly Ruby) in Go, and that code is kind of notoriously uncooperative with the rest of the ecosystem and hard to maintain. You'll find it in Go web stack libraries, in particular, or with code for people who tried to fake generics with codegen.
- preseinger 5y agoExample? The majority of Go I've been hired to improve has suffered immensely from not using accepted idioms. Most of my work has been focused on introducing those idioms systemically.
- the_duke 5y agoEmulating sum types in languages without pattern matching is extremely awkward, to the point of being almost useless. type Result[T] struct { ok: *T err: error } Great, so how do you work with it? * You can have a `ok()` getter that returns `*T`. Now you you need an `if x != nil` * `isOk()` + `isErr()` * `unwrap() T`, which panics on errors * `split() (*T, err)` that splits into separate values, especially awkward since you both need `if err != nil` AND dereference the pointer That API is more awkward then the status quo, and doesn't buy you any correctness guarantees because eventually you have to do an `if x := res.ok(); x != nil` or `x, err := res.split(); if err != nil {` anyway. Pretty much the only convenience you gain are functions like `map()` or `unwrap_or`, but eventually you always have to take out the value and without being able to pattern match you can't get an API that improves correctness much. (std::optional in C++ is a great example)
- kbd 5y ago> because eventually you have to do... Emphasis on eventually instead of at every single function call.
- linkdd 5y agoThe Rust Result type has: - map / map_err - and_then / or_else - unwrap / unwrap_or And countless of other functions making it very practical to chain computations without having to pattern match anything. I do the same in Erlang/Elixir. In Golang, I need to check every function call, and if I want to know where an error come from, I need to wrap it in an errors.New() because no exceptions = no stacktrace
- kbd 5y ago> and if I want to know where an error come from, I need to wrap it in an errors.New() because no exceptions = no stacktrace Zig manages to provide traces with very little overhead: https://ziglang.org/documentation/master/#Error-Return-Traces https://ziglang.org/documentation/master/#Error-Return-Trace... I am baffled as to why error handling in Go remains so impoverished.
- egeozcan 5y ago
- Thaxll 5y agoWell it's better than most language with exceptions. People makes it a big deal, in reality it's not.
- linkdd 5y agoAn exception has a stacktrace. This single piece of information is crucial when you debug and makes handling errors in Golang embarrassing. I rest my case.
- mseepgood 5y agoIf you want stacktraces just panic. But that's not proper error handling.
- divan 5y agoOr just print stacktrace with `debug.PrintStack()`
- pkaye 5y agoStack trace was part of a proposal to add to the error package but it didn't happen. You can use third party errors packages like https://github.com/pkg/errors https://github.com/pkg/errors which wrap errors with stack traces.
- randomdata 5y agoGo exceptions do carry stack trace information. However, the topic is errors, not exceptions. Those are very different concepts. Of what use is stack trace information in debugging values that you have assigned error meaning to when not other types of values? If you had a function func add(a, b int) int { return a * b } there would be no expectation of carrying a stack trace to debug it. So what's different about func add(a, b int) error { return errors.New("cannot add") } that does require a stack trace?
- dragonwriter 5y ago
- rp1 5y agoI’ve been doing a lot of rust programming recently, but how exactly is the Result monad better? I feel like I end up with nested match statements for chained results, but maybe I’m doing something wrong.
- linkdd 5y agoRely more on the map/map_err/or_else/... methods for the Result type. You'll get something like (pseudocode): may_fail_who_knows() .map(use_value) .map_err(some_error_processing) .and_then(another_computation_which_can_fail) .or_else(with_some_error_handling_that_can_rescue) .unwrap_or(a_default_value) Basically, instead of nested match expressions, you get a "pipeline".
- rp1 5y agoThat does clean stuff up a bit, but still requires nesting if you need to access more than one value generated in the chain at a time. Go's pattern of val, err := ... is a little cleaner in that regard, but does have a lot of redundant if err != nil checks.
- stouset 5y agoAnd let result = may_fail_who_knows()? .use_value_which_might_also_fail()? .use_a_different_way_that_might_fail()?;
- andrewjf 5y agoI would love to use this style but I find myself reverting back to the match syntax. match may_fail_who_knows() { Ok(success) => { do_something_with_success(success)? }, Err(failed) => { some_error_processing(failed) } } As `do_something_with_success` in a closure can't early return from the function (since it's in a closure), which makes sense, but just annoying to read nested results.
- wmanley 5y agoThis is equivalent to: let x = match may_fail_who_knows() { Ok(y) => Ok(another_computation_which_can_fail(use_value(x))), Err(e) => with_some_error_handling_that_can_rescue( some_error_processing(e)), }; match x { Ok(y) => y, _ => a_default_value, } It's a bit more verbose than using the combinators, but someone coming across it for the first time will understand it immediately because there's less to remember to understand it (this is where go really shines). Also: by avoiding functors there are fewer subtle lifetime issues and `move ||` stuff to deal with and you can return from the containing function and use the `?` operator. During the discussions of how `.await` was going to work for rust async there was the proposal to add other suffix keywords. So this would look like: may_fail_who_knows() .match { Ok(y) => Ok(another_computation_which_can_fail(use_value(x))), Err(e) => with_some_error_handling_that_can_rescue( some_error_processing(e)), } .match { Ok(y) => y, _ => a_default_value, } Maybe not that different.
- hbn 5y agoIt's definitely more streamlined, but my concern would be that newcomers might not understand that it's just an alias. Learning Go really reframed my concept of what an interface is, and I thought that using an empty interface to represent "any type" was kind of ingenious, and helps reinforce its ethos. An empty interface can represent any type because every type inherently implements an interface with no methods. And that's what Go is all about -- implicitly implementing interfaces. If a newbie hops into Go and just starts using "any" I think they might assume it's a magic type that's at the base of everything, missing out on the fact that they're still taking advantage of interfaces.
- egeozcan 5y agoI kind of understand what you mean, as it's similar to what happened with prototypes and the late-comer class syntax in JavaScript, and in that case, it was a net positive change as most would probably say. Let's see how this plays out.
- pawelmurias 5y agoA language shouldnt be optimized towards doc avoiding newbies at the cost of making things verbose and ugly.
- jshen 5y agoGo is the most readable language I’ve used in terms of understanding other peoples complex code bases, and I’ve been doing this a long time and have used a lot of languages. Edit: It’s also one of the most approachable languages.
- valenterry 5y agoWhy don't you list the languages that you have used? Otherwise there isn't really any new information. For example for, having done Java, Scala, Python, Groovy, Haskell, Typescript and a couple others, Go reads extremely horrible. It feels as bad as enterprisey Java to me.
- nvarsj 5y agoYes, please, the iferr pattern drives me nuts. While I built a few years of my career on golang, I'm absolutely sick of the language at this point. At least we finally get generics.
- politician 5y agoWriting code for the happy path is 1/100th of programming. Maybe less.
- lanstin 5y agoThere is a reason very robust software is written in C: no exceptions means you think about normal errors all the time and reason about what the difference bwtween an error openjng the file and an wrror writing the file. Sqlite, linux, cpython, all bullet proof and all C. The lack of exceptions more than makes up for memory unsafety issues.
- pjmlp 5y agoBullet proof you say, https://www.cvedetails.com/vulnerabilities-by-types.php https://www.cvedetails.com/vulnerabilities-by-types.php
- Cthulhu_ 5y agoWhat alternative do you propose?
- nvarsj 5y agoThere's many options, just look at other modern languages. The simplest are exceptions/errors that can be thrown - like you get in Java, python, typescript, etc. For something more like a traditional return value, you have std::result in rust, and error union types in zig, with language support to guarantee you check and handle the result. In C, you have macros and goto that can be used to reduce boilerplate and implement whatever pattern you want.
- kubb 5y agoI would also like discriminated unions, and a more sane approach to code generation than go generate and writing your own binary that parses source and spits out source code.
- rp1 5y agoThe Go code parsing libraries are quite good though. Not sure what else you could want re. code generation.
- kubb 5y agoBasically macros, like in Rust, not like in C. Rust can do stuff like the serde serialization library while Go has to rely on reflection with its obvious performance drawbacks.
- rp1 5y agoGo could do something similar if you're willing to run `go generate` as part of your build process. For most Go applications, the reflection overhead is a fine price to pay for convenience, just like GC is a fine price to pay for not having to deal with the borrow checker. Obviously, these tradeoffs don't hold for all programs, but Go has definitely found a niche.
- kubb 5y agoI'm not sure what you're arguing with. I know these things. I'm still missing the ability to have efficient serialization (as one example) easily. It matters.
- rp1 5y agoI’m not arguing. I was just highlighting that you could easily implement a json library that uses go generate instead of reflection. I was positing that such a library hadn’t been made yet because the perf hit of reflection is fine for most people. Also, just an aside, if perf is a concern than JSON isn’t a good choice to begin with.
- BatteryMountain 5y agoCan you kindly post a snippet of what you want the early return on error syntax sugar to look like? I'm trying to map it in my mind on how I'd do it in C# and how it would look in Go.
- Cthulhu_ 5y ago> all we need is sugar syntax for early return on error -- like more modern languages such as Rust and Zig have -- and Go may finally be pleasant to program in! Can you provide an example? What's wrong with `return err`? That's a very explicit early return on error, no magic or language features (= added complexity) needed.