Go 1.22 released
Posted Feb 7, 2024 17:31 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (28 responses)
Posted Feb 7, 2024 22:11 UTC (Wed)
by milesrout (subscriber, #126894)
[Link] (13 responses)
Posted Feb 7, 2024 22:13 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (3 responses)
Nope. No such promises were made.
And error handling in Go is just fugly.
Posted Feb 8, 2024 12:28 UTC (Thu)
by milesrout (subscriber, #126894)
[Link] (2 responses)
Posted Feb 8, 2024 13:09 UTC (Thu)
by timon (subscriber, #152974)
[Link] (1 responses)
Posted Feb 8, 2024 20:24 UTC (Thu)
by milesrout (subscriber, #126894)
[Link]
Posted Feb 8, 2024 1:47 UTC (Thu)
by pj (subscriber, #4506)
[Link]
[citation needed]
...for instance, I think Zig does it better, though in the end it's a matter of opinion.
Posted Feb 8, 2024 11:58 UTC (Thu)
by georgm (subscriber, #19574)
[Link] (7 responses)
if function returns "(foo, error)" or just "error":
You are not forced to use this pattern, so both the current and the new style would be fine and can coexist
Posted Feb 8, 2024 12:26 UTC (Thu)
by milesrout (subscriber, #126894)
[Link] (6 responses)
I think the worst part of it, though, is that it creates special-case syntax for what is should just be general programming. Go supports multiple return values and sometimes those return values include errors. Sometimes they don't. The nice thing about programming languages is that they are general. You use the same simple mechanisms for everything. Go is particularly good about this, almost as good as C. In C, almost _everything_ you do is arithmetic, dereferencing, taking the address of something, for/while/if/switch or calling a function. In Go you can add '<-' and 'go'.
Why add new syntax to do something that is so simply achieved with a simple 'if' statement? It reminds me of people suggesting that we all need to use 'map', 'filter' and 'reduce' functions and complicated iterator schemes. Do we? What's wrong with a 'for' loop with a nested 'if' structure controlling a line of code that modifies a variable? More than sufficient, I think. Just to save a few characters? Making code more dense isn't necessarily a good thing. Error handling in Go gives your code a nice bit of breathing room, I think. Code in more dense languages just needs more blank lines to space it out, IMO.
Basically, I think syntactic sugar is generally bad.
Posted Feb 8, 2024 13:15 UTC (Thu)
by Cyberax (✭ supporter ✭, #52523)
[Link] (3 responses)
This argument is as stupid as anything. "We can't fix stuff because fixing stuff would make our old stuff look bad". So there.
Posted Feb 8, 2024 13:25 UTC (Thu)
by Funcan (subscriber, #44209)
[Link] (1 responses)
There's the change in this release for variable reuse in for loops, which removes the need for an ugly idiom in new code, but which will have to stay in coffee intended for older compiler versions, for example.
Adding genetics also meant that some old code became less idiomatic, but it was considered a big enough win.
Posted Feb 8, 2024 21:29 UTC (Thu)
by Cyberax (✭ supporter ✭, #52523)
[Link]
It might eventually start _looking_ strange to new Go developers, but that's already true for other kinds of code. For example, very old Go code had no `context.Context` (so it was not interruptible) and even used "goto err" pattern (see: https://cs.opensource.google/go/x/net/+/master:websocket/... ).
Posted Feb 8, 2024 20:23 UTC (Thu)
by milesrout (subscriber, #126894)
[Link]
Posted Feb 8, 2024 13:35 UTC (Thu)
by georgm (subscriber, #19574)
[Link]
The "ugly" depends on personal preference.
Now with type "any" - you have that split already. Or should we continue using interface{} all over the place to not have new and legacy code? And generics?
> You use the same simple mechanisms for everything. Go is particularly good about this, almost as good as C
Yes, as good as C to not bring any safety measures or syntax help:
Wait? You have to "make(map[foo]bar)" (or map[foo]bar{}) before using it (but slices are okay to not exist, simple).
Wait? You want to use it in parallel go routines (not even in our code, but for example the http router)? Don't use it. It is unsafe, or (like in general in go) - add a separate lock to use it.
But you could use sync.Map for this. But then forget about the types (even if go supports generics by now).
Simple might be too simple or too error-prone. No one is hindering you on using this and nobody complains (until something crashes).
Just one example of "simple". There are a lot of other rough "simple" edges.
Posted Feb 12, 2024 13:15 UTC (Mon)
by mathstuf (subscriber, #69389)
[Link]
Because these functions separate the "what" from the "how" nicely. I don't have to seek out an extra or missing `!` or half-done modus ponens bug hidden in an `if` condition and change my thoughts based on whether the body contains `break` or `continue`. Instead, I get `until`, `while`, or `filter` as a name. Are we mutating the container? The values? Building a new structure or just making a pass over it? These are the kinds of questions Rust's `Iterator` trait methods answer for me directly. Then I can focus on *what* the code is doing overall without having to squint through a haze of partially-remembered open-coded intro-CS-course algorithm loops.
C++ also does it OK, but they don't compose all that well. Ranges does better but I've not had the chance to use them in anger yet.
Posted Feb 8, 2024 13:14 UTC (Thu)
by michaelkjohnson (subscriber, #41438)
[Link] (13 responses)
This is, for example, something that Python got fundamentally wrong (see os.exists and the many wrappers written to not take the expensive exception case for file does not exist, which is properly neither an error nor an exception), and when I learned Go I was delighted that Go did not perpetuate this particular class of design error.
Go error handling is an example of the finely-tuned design sense that its creators brought to the language.
Explicit is better than implicit, and errors are not exceptions. Anyone who thinks errors are exceptional hasn't been paying attention. ☺
Posted Feb 8, 2024 13:21 UTC (Thu)
by Cyberax (✭ supporter ✭, #52523)
[Link] (12 responses)
I'm not talking about panics, the modern consensus is that exceptions are a bad idea. Go has some strange ideas about `recover`, but whatever, it's at least reasonable.
I'm talking about `if (err != nil) {return nil, err;}` statements that can easily take up half of all the code in functions that call a lot of other functions. They are unergonomic, and they prevent chaining.
Posted Feb 8, 2024 14:06 UTC (Thu)
by farnz (subscriber, #17727)
[Link] (4 responses)
And given that this is a common pattern in existing Go code, why not have a special syntax that implements this form of error handling for you? Something that converts:
Posted Feb 8, 2024 20:27 UTC (Thu)
by milesrout (subscriber, #126894)
[Link] (2 responses)
Posted Feb 9, 2024 10:17 UTC (Fri)
by taladar (subscriber, #68407)
[Link]
Posted Feb 9, 2024 10:34 UTC (Fri)
by farnz (subscriber, #17727)
[Link]
The point of brevity in a programming language is to make doing the right thing easier than doing the wrong thing. If my "happy path" (the path I take if there are no errors) is hidden away in a sea of error handling, then it becomes hard to see whether or not the happy path does the right thing, because I've got to read my way around the error handling in order to actually see the happy path.
If you come up with a simple way to do the right thing, people will use it. Else, you have to keep an eye out for accidental mistakes - e.g. typing fooR, fooE := getFoo(); if fooR != nill { return fooE; }; sort of thing, where I'm using ; to mark newlines.
Posted Feb 8, 2024 21:21 UTC (Thu)
by Cyberax (✭ supporter ✭, #52523)
[Link]
I tried some manual experiments, and the resulting code looks _so_ _much_ _more_ _readable_.
Posted Feb 8, 2024 14:13 UTC (Thu)
by michaelkjohnson (subscriber, #41438)
[Link]
(It's also possible to embed errors instead and have explicit handling in chains, so they don't exactly *prevent* chaining, but I agree that it is reasonable to argue that this is not the ergonomic best implementation of chaining.)
So my complaint isn't related to your concerns, and I appreciate the clarification!
Posted Feb 9, 2024 5:46 UTC (Fri)
by quotemstr (subscriber, #45331)
[Link] (3 responses)
No it isn't. There's a small, vocal community that says so, but that doesn't make it right, and there are plenty of new exceptional systems being made.
Posted Feb 9, 2024 14:27 UTC (Fri)
by farnz (subscriber, #17727)
[Link] (1 responses)
Out of interest, are they avoiding the two big problems I have with legacy exception-based languages:
Posted Feb 9, 2024 15:55 UTC (Fri)
by michaelkjohnson (subscriber, #41438)
[Link]
The Go "Error" type is an interface that fits into Go's normal type system:
https://go.dev/blog/error-handling-and-go
For an understanding of panic and recover, see:
Posted Feb 9, 2024 16:17 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link]
Posted Feb 10, 2024 15:43 UTC (Sat)
by mathstuf (subscriber, #69389)
[Link] (1 responses)
This is bad anyways. Making sugar for bad idioms will only rot your teeth (same as blind `?` error bubbling in Rust). What is missing is the context that made the error happen. I find "file not found" from a high-level API useless. "Git repository expected; no .git found" is actually useful. Better as an enum so that code can inspect and adapt instead of low-level code assuming that a pre-baked string for user consumption is the only suitable representation.
Posted Feb 10, 2024 20:37 UTC (Sat)
by Cyberax (✭ supporter ✭, #52523)
[Link]
Now Go needs to work to make it more idiomatic.
Posted Feb 7, 2024 19:51 UTC (Wed)
by rbranco (subscriber, #129813)
[Link] (1 responses)
Posted Feb 7, 2024 19:52 UTC (Wed)
by corbet (editor, #1)
[Link]
Posted Feb 7, 2024 22:21 UTC (Wed)
by milesrout (subscriber, #126894)
[Link] (1 responses)
This is a classic issue. When I was just a beginner, I stumbled into the same problem in Python. A callback being constructed in a loop (maybe a list comprehension?) using 'lambda', which captures the loop iterator. The solution is clever: instead of [(lambda: i) for i in range(10)], you can do [(lambda i=i: i) for i in range(10)]. The default argument is evaluated when the lambda expression is evaluated. It looks odd, but it works.
>>> l1 = [(lambda: i) for i in range(10)]
Posted Feb 8, 2024 0:33 UTC (Thu)
by iabervon (subscriber, #722)
[Link]
That looks less odd than what I'd have thought of, which is: But I think I prefer: The fact that you're creating a list of functions that are different due to capturing different values is worth emphasizing by making it a named function.
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
a "?" on a function which returns (bar, error) would return (default, error) or only error on error
example: file := os.Open("file.txt")?
a "?" on a function which return only error woud return (default, error) only error on error
example: os.Mkdir("testdir", os.ModePerm)?
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
You need a map: use map[foo]bar
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
into a shorter form - like varFoo := GetFoo() orReturnErr, maybe using a sigil for orReturnErr?
varFoo, err := GetFoo()
if err != nil {
return err
}
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
Go 1.22 released
It looks like they have a nice RSS feed ... yes, we'll add it, thanks.
Newsletter
Loop variables shared between iterations - not just in Go
>>> l2 = [(lambda i=i: i) for i in range(10)]
>>> [f() for f in l1]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>> [g() for g in l2]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Loop variables shared between iterations - not just in Go
[(lambda i: lambda: i)(i) for i in range(10)]
def make_callback(i):
return lambda: i
[make_callback(i) for i in range(10)]