8 ms·
Looks like they really plan to move forward with the `try` function... I'm personally not a fan of it, I hope they listen to the feedback they received on the G
by AtroxDev 7y ago
Looks like they really plan to move forward with the `try` function... I'm personally not a fan of it, I hope they listen to the feedback they received on the GitHub Issue and not just push it through like the error inspection [0].
[0]: https://github.com/golang/go/issues/29934#issuecomment-489682919 https://github.com/golang/go/issues/29934#issuecomment-48968...
- kenhwang 7y agoIt looks like instead of making a better error handling system, they just made it easier to not type `if err != nil` everywhere. Then there's all that handler stuff that looks very much like Java's `try ... catch` in reverse order. Pretty underwhelming for what's supposed to be a modern language.
- tjholowaychuk 7y agoWhich can be solved with a snippet bound to `e`.
- eikenberry 7y agoThe problem `try` is addressing isn't typing the boilerplate, it is that it clutters up the code making it harder to read.
- dnautics 7y agoit's not as if `if err != nil` doesn't clutter up the code!
- eikenberry 7y agoSorry, I said that poorly. I mean that the problem `try` is fixing is that all the `if err != nil ..` clutters up the code and they are introducing `try` to clean that up.
- zellyn 7y agoIs Go “supposed to be a modern language”? I'm not sure even the language designers would agree with that characterization.
- Thaxll 7y agoWhat is a modern language anyway?
- BlueTemplar 7y agoWell, for one, it handles text as being UTF-8 by default, even at the lowest level (and in no case assumes that byte = character !)
- pjmlp 7y agoOne that takes into account features that have made into the mainstream during the last 20 years, instead of feeling like an Algol-68 subset.
- Thaxll 7y agoI don't care about the features in the last 20 years if I'm able to do my job efficiently which Go as a language provides. Never wonder why those great academics languages with a ton of features are not adopted?
- jerf 7y ago"Then there's all that handler stuff that looks very much like Java's `try ... catch` in reverse order." The key characteristic of Go's error handling is that you have to handle errors in the scope in which they occur, vs. exception handing which is designed around throwing errors up the stack until something finally handles it, often quite distant from the point of the error and lacking context. "try" just re-spells that. It isn't a step towards exception handling; it's exactly as "exception-handle-y" as if err != nil { return err } already is, whatever value you may consider that to be. Part of the goal is to make correct handling where you actually do something with the error that much easier, instead of having to do something essentially unrefactorable for every error, through a combination of allowing error handling to be factorable in this new scheme, and some other changes to errors to add more structure by convention to create official ways of composing them together and examining these composed errors in sensible ways.
- pacala 7y agoThe confusing part is the underlying assumption that there exists a way to "handle" errors, understood as a way to workaround the error and somehow continue execution. This is most of the times futile, there is nothing to "handle", other than abort the execution and report the condition. For example, consider a bug that causes a data structure invariant to be violated. The correct "handling" of the situation is to fix the bug and rerun the code, not add layers and layers of error "handling" code ahead of time.
- tptacek 7y agoThat's literally the case that "panic" is there to handle.
- pacala 7y agoThe example was a worst case example. There are weaker versions thereof, for example the familiar: def handle(request): try: return Response(200, process(request)) except UserError as ex: return Response(400, ex.message) except InternalError as ex: return Response(500) The principle stays the same, there is not much to "handle" and no option to recover without external help, either by providing a well-formed request for 400 errors or by providing well-behaving code for 500 errors.
- pjmlp 7y agoIt still needs to catch up with CLU, released on 1973.
- AnimalMuppet 7y agoIn what way?
- pjmlp 7y agoGenerics to start with.
- staticassertion 7y agoGo is new, not modern.
- blaisio 7y agoThey aren't really moving forward with it, they are implementing an experimental version of it so people can try it in real code. Many things are added temporarily when a new version is in development. Eg. the error handling changes they mentioned were implemented, and then later most of it was removed before the feature freeze. So there's still plenty of time to stop the proposal.
- butterisgood 7y agoIt looks like a monad for Either or Result to me, and I think it's an improvement. The good news is if you hate it you don't have to use it, but since it'll be in the language (if they take it) you'll have to know how it works. I don't think this is worse than things like "for:else" in Python.
- jzoch 7y agoInteresting to go with Monad "by convention". They wont build in an explicit type but rather utilize convention (placing err as the last param and giving it a certain shape) to replicate that behavior
- butterisgood 7y agoYep. Being practical and simple with easy to remember rules seems to be more of a value to the designers than to build a language with “features from Ivory Towers” (in the Haskell sense anyway). The designers hope to give Go users “what’s needed to write good code” without having the learning curve of other languages. I suspect this balance is hard to strike at times and I rather admire their efforts.
- the_duke 7y agoThis looks pretty much exactly like the old try!(expr) macro in Rust, which has since been replaced with the postfix `?` operator. One crucial difference is that the proposal uses a named `err` return value that can be manipulated in defer. This is supposed to allow cleanup and wrapping of the error type. Rust solves wrapping is by allowing auto-conversion between error types (if they implement it). My first intuition is that it would be an improvement over omni-present `if err != nil {}`, but it feels somewhat awkward and tacked on. Especially the mutable `err` return value. Of course there also was a reason why try! was replaced with `?`: awkward nesting and chaining. Go would have the same problem.
- zeeboo 7y agoTo be clear, you can name the return value whatever you want. People just typically name their error values `err`. Also, the mechanisms to mess with named return values in defer already exist: https://play.golang.org/p/7nFyiuAa3Ra https://play.golang.org/p/7nFyiuAa3Ra
- the_duke 7y agoJust to clarify, I was aware that named returns already exist in Go. My main point is that this somewhat weird pattern is encouraged by the proposal since no other wrapping mechanism exists. And IMO wrapping is really essential for debuggable code.
- zeeboo 7y agoI totally agree that good error wrapping is essential. In fact, I've been using this exact system for wrapping my errors for years, and it's been great (https://godoc.org/github.com/zeebo/errs#WrapP https://godoc.org/github.com/zeebo/errs#WrapP). I suspect it's only "somewhat weird" due to lack of familiarity, and that adding an additional mechanism to wrap errors when one already exists is not in the spirit of a language built from small orthogonal components.
- the_duke 7y ago
- saturn_vk 7y agoWhat the hell happened to the catch/handle draft proposal. That looked a lot more useful
- ngrilly 7y agoIt was abandoned in favor of the try proposal, mostly because the handle statement was coming partly redundant with the defer statement, but with subtle differences.
- allochthon 7y agoThe statement looks like an assignment, and unless I was really used to seeing it, I would never expect an implicit return.
- atombender 7y agoMy main beef with this and other proposals is that they don't clear up a fundamental flaw in Go: Multi-value returns with errors as a poor man's sum type. Most functions in Go have a contract that the return values are disjoint, or mutually exclusive — they either return a valid value or an error: s, err := getString() if err != nil { // It returned an error, but "s" is of no use } // s is valid This pattern so ingrained that there's hardly a single Go doc comments on the planet that says "returns value or error". The same goes for the "v, ok := ..." pattern. But this is just a pattern and not universally true. A commonly misunderstood contract is that of the `Read` method of `io.Reader`, which says that when `io.EOF` is returned, the returned count must be honoured. This is an outlier, but because the convention of disjointness is so widely adopted, many developers make this assumption (it's trivial to find repos in the wild [1] that make this mistake), and so this is, in my opinion, bad API design. (As an aside, it's also true that multi-value returns beyond two values almost always become cumbersome and impractical, especially if said values are _also_ mutually exclusive. Structs, having named fields, are almost always better than > 2 return values.) This kind of careless wart is typical of Go, just like other surprising edge cases like nil channels (or indeed nil anything). I would much rather see a serious stab made at supporting real sum types, or at least mutually exclusive return values. For example, I could easily see this as being a practical syntax: func Get() Result | error { ... } This union syntax showed up in the Ceylon language, and it's a neat pattern for a conservative language that doesn't want to venture into full-blown GADTs. Such a syntax would be a much better match for a try() function, since there's no longer any doubt about the flow of data — there's never a result returned with an error, it's always either a result or an error: result := try(Get()) or simply support existing mechanisms for checking: if err, ok := Get().(error); ok { ... } if result, ok := Get().(Result); ok { ... } switch t := Get().(type) { case Result: // ... case error: // ... } I'd love to see a `case` syntax that allows real local variable names: switch Get().(type) { case result := Result: log.Printf("got %d results", len(result.Items)) case err := error: log.Fatal(err) } And of course, you could have more than two values: switch Get().(type) { case ParentNode: // ... case ChildNode: // ... case error: // ... } The Go compiler can be strict here and require that every branch be satisfied or that there's a default fallback, although some might prefer that to be a "go vet" check. A full-blown sum type syntax would be awesome, though I know it's been discussed before, and been shot down, partly for performance reasons. Personally, I think it's solveable. I'd love to be able to do things like: type Expression Plus | Minus | Integer type Plus struct { L, R Expression } type Minus struct { L, R Expression } type Integer struct { V int } [1] https://github.com/search?q=%22read%28%22+%22if+err+io.EOF%22&type=Code&l=Go https://github.com/search?q=%22read%28%22+%22if+err+io.EOF%2...