Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign upproposal: errors/errd: helper package for deferred error handlers #32676
Comments
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
I don't think Apply carries its weight. (It seems helpful here largely to describe how Add and Wrap work.) The example code isn't quite right; I think you meant:
But if I were going to do that, I would instead write
That is clearer and faster: it only need refer to the single If you had a more complicated function to apply, it seems fine to just make it take a
The version without Apply isn't obviously better here, as above, but it's not clearly worse either. |
This comment has been minimized.
This comment has been minimized.
This is probably too bike-shed-y at this early phase, but I wonder if there is a better name than Trace since the concept of "trace" has an unrelated antecedent in the Go library and runtime. Perhaps:
|
This comment has been minimized.
This comment has been minimized.
Following two sample codes missing err in last argument.
|
This comment has been minimized.
This comment has been minimized.
Thanks @rsc, this looks great to me. Personally this addresses the only remaining concern I had with the main |
This comment has been minimized.
This comment has been minimized.
Whether or not try() is going to be implemented, this errd package is a great idea, because, I have to admit, that after thinking about it, using defer for error handlers makes sense in many cases, as long as performance is not critical. Unlike @cespare I think the One use case which seems to be missing is logging the error using the log package. I think and errd.Log() function could be very useful. However, I think there is one way that would make this even more useful and that would be to make it a "fluid" API, a bit like this: package errd // import "errors/errd"
type errd * error
func New(err * error) errd {
return errd(err)
}
func (e errd) Trace() errd
func (e errd) Log() errd
func (e errd) Logf(fmt string, args...interface{}) errd
func (e errd) Apply( f func(err error) error) errd
func (e errd) Add(errp *error, format string, args ...interface{}) errd
func (e errd) Wrap(errp *error, format string, args ...interface{}) errd Then we can easily compose error handlers like this: |
This comment has been minimized.
This comment has been minimized.
What is the rationale for the name? |
This comment has been minimized.
This comment has been minimized.
I personally like the idea of |
This comment has been minimized.
This comment has been minimized.
Simple question as I am obviously missing something. Why a pointer to Also, does |
This comment has been minimized.
This comment has been minimized.
Why do you feel the need for a separate package, rather than putting these in I don't see why
Shouldn't the two formatting functions end in I've seen the name |
This comment has been minimized.
This comment has been minimized.
I made a quick hack and the results are interesting: https://gitlab.com/beoran/errd So now I can handle an error with where closer has a Close method like this: The potential is limitless, there could even be .If(), .Switch(), so the error handling could get as complex as I would like it to be. But I guess not everyone likes method chaining, so maybe it's better that this remains an alternate package, much like how we have std lib log and logrus. |
This comment has been minimized.
This comment has been minimized.
@jba, I would expect everything in errors to be about errors and not about error pointers. Putting the deferrables in their own package lets them have a different name space. I think that solves one of the big naming problems we had with clunkers like fmt.HandleErrorf. |
This comment has been minimized.
This comment has been minimized.
@cespare, I agree that Apply doesn't carry its weight for func literals. It might if people write other (named) helper functions. |
This comment has been minimized.
This comment has been minimized.
@johanbrandhorst, Yes, errd is short for err-defer. If these are going to be used a lot, it makes sense for them to have a short name (like fmt.Printf not format.Printf). And if the package were errors/deferred then the uses would look like:
which is a bit repetitive. |
This comment has been minimized.
This comment has been minimized.
It only helps insofar is it allows such helpers have the signature
So even then it seems like a wash. I'd rather establish the convention that custom error helpers have the signature |
This comment has been minimized.
This comment has been minimized.
The name |
This comment has been minimized.
This comment has been minimized.
Of course, that stutters ( |
This comment has been minimized.
This comment has been minimized.
I read the name as "erred", as in to err --- someone erred, now it's time to deal with consequences. |
This comment has been minimized.
This comment has been minimized.
I am generally in favor of this proposal, but share concerns about the name. That said, it's not an easy package to name! I'm wondering about defer handle.Trace(&err)
defer handle.Add(&err, "copy %s %s", src, dst)
defer handle.Wrap(&err, "copy %s %s", src, dst) |
This comment has been minimized.
This comment has been minimized.
I see several people dislike my experiment. I have a feeling why this might be so, although I'd prefer to hear why. After all, if the idea of doing error handing using |
This comment has been minimized.
This comment has been minimized.
I'm actually fond of fluent APIs myself, but I don't think this one is necessary. In the relatively rare cases where you need multiple error handlers, you can just write multiple Another problem is that the only way to add another action is to modify your package. |
This comment has been minimized.
This comment has been minimized.
Thanks! I'd like to see this as part of errors. |
This comment has been minimized.
This comment has been minimized.
This would also make easier to add some simpler way to defer error handlers at a later stage. For example, try may have the signature (in pseudo-code): func try(expr, handlerFunc, args ...) (T1, T2, … Tn) with the added benefit that it would not be needed to name return values. I think it is obvious enough how this would work (and this issue is not the right place to discuss it) but I think this is a nice example of why a more consistent definition is desirable, instead of having the choice between |
This comment has been minimized.
This comment has been minimized.
@beoran as someone who does not have the spare time to ramp up 100% on this new error handling stuff, the current error handling proposals are frankly, terrifying to me from a complexity standpoint if the above statement is accurate. Note that my perspective coming from that of a new user looking at this for the first time, maybe second time. I would be very surprised if other people did not share this opinion under the premise that they did not want to aimlessly re-iterate this concern, especially without concrete feedback and reasoning for their claims. I thought your experiment was useful, even though I did not agree with it, because it was able to provide a discussion point of what could happen once other users started to adopt this idiom. Food for thought: my initial reaction to this is simply the question of how this is better than exception handling as a whole. In addition to looking very complex, it also looks unfamiliar. |
This comment was marked as off-topic.
This comment was marked as off-topic.
I am happy to see improvements to the code below.. if err != nil {
return err
} Surprisingly, seeing a niche language (zig) also uses try to reduce the if err != nil code. var args_it = process.args();
const exe = try unwrapArg(args_it.next(allocator).?);
var catted_anything = false;
var stdout_file = try io.getStdOut();
while (args_it.next(allocator)) |arg_or_err| {
const arg = try unwrapArg(arg_or_err);
if (mem.eql(u8, arg, "-")) {
catted_anything = true;
var stdin_file = try io.getStdIn();
try cat_file(&stdout_file, &stdin_file);
} else if (arg[0] == '-') {
return usage(exe);
} else {
var file = File.openRead(arg) catch |err| {
warn("Unable to open file: {}\n", @errorName(err));
return err;
};
defer file.close();
catted_anything = true;
try cat_file(&stdout_file, &file);
}
}
if (!catted_anything) {
var stdin_file = try io.getStdIn();
try cat_file(&stdout_file, &stdin_file);
} I am more than 10 hours per day using go, try joining will make golang not look good before, can you change words or symbols? If I join try, I only want to use golang for 5 hours a day.I really don't want to see the full screen try |
This comment was marked as resolved.
This comment was marked as resolved.
@guonaihong I think you meant to put your comment on #32437, not this issue. I've marked your comment as off-topic. |
This comment has been minimized.
This comment has been minimized.
thanks for your reminder. |
This comment has been minimized.
This comment has been minimized.
This package (or another) could also add a handler for closing, like Close(errp *error, io.Closer) to do proper error handling when closing a writer or even to solve the "panic problem" for CloseWithError. The point is, error handling like this may be a bit odd, but it allows us to solve a few subtle gotchas in an idiomatic and straightforward way. |
This comment has been minimized.
This comment has been minimized.
@mpvl, yes I already added a |
This comment has been minimized.
This comment has been minimized.
As someone pointed out on another issue, d, err := someFn()
try(errors.Wrap(err, "some msg")) To support this use-case, maybe these functions should return an error value. |
This comment has been minimized.
This comment has been minimized.
I'll chime in with greatly preferring func() (err error) {
defer errd.Apply(&err, func(err error) error {
// this check will apply to errs from both fn1, and fn2's potentially-applied-err,
// and any new code that appears below it in the future
if err == ... {
err = ...
}
})
_, err = try(fn1())
defer errd.Apply(&err, func(err error) error {
// currently only applies to fn2's err
if err == ... {
err = ...
}
})
_, err = try(fn2())
} It seems much safer and easier to me to pass the same fn to |
This comment has been minimized.
This comment has been minimized.
In the Go 2 Error Values proposal (#29934), one of the most common complaints I saw was the lack of an explicit |
This comment has been minimized.
This comment has been minimized.
I was hoping it also returned the error so you can use it in a plain return also?
|
This comment has been minimized.
This comment has been minimized.
This should probably be split into two distinct packages
The part of My biggest concern is that |
This comment has been minimized.
This comment has been minimized.
What is the fate of this proposal now that #32437 has been declined? It is of course still possible to use |
This comment has been minimized.
This comment has been minimized.
Given that |
The try proposal (#32437) uses a placeholder
fmt.HandleErrorf
to annotate errors.We do not intend that to be the actual function name. It was a blank to be filled in.
Here is one possible way to fill in that blank.
Thoughts and improvements welcome.
/cc @mpvl @jba @neild