On 6 February 2014 22:00, Nate Finch <nate.fi...@canonical.com> wrote: > So, I took a deeper dive into Tim's newly renamed arrar package, and > compared it to Roger's errgo package. > > Before I go into the implementations, I want to write down some goals: > > 1: Make it easy to wrap errors from deeper in the code with context from the > local scope. > 2: Mask errors that are merely implementation details from callers. > 3: Give a stack trace of the root error for debugging purposes. > > The packages are very similar in what they're trying to do - maintain an > ordered list of errors that represent one logical error to the end user, and > make it easy to wrap an existing error in one of their special errors, > without losing information about the wrapped error. > > I think both of them need examples to show how they're intended to be used, > but once I played around with them in actual code, they made sense. I also > think both of them could use with some name tweaking to make the intent of > the code a little more obvious. > > I like that Roger's package has a concrete type that can be reused, and > interfaces you can implement if you don't want to use the base type. I would > tend to do the former a lot, something like > > type NoConfigurationError errgo.Err > > However, the concept of a Diagnosis seems overly complicated to me. I > pretty much never want to encourage people to look at the underlying error, > and diagnosis seems to want to do that in some cases.
To clarify a little: errgo.Diagnosis(err) is almost exactly equivalent to arrar.GetErrorStack(err)[0], and both are a necessary and important way to access the underlying error when it contains information important to the caller. Custom error types in Go can contain useful info and are an important part of the language. I don't really see there's a significant difference between if errgo.Diagnosis(err) == io.EOF { and if arrar.Check(err, func(err error) bool {return err == io.EOF}) { The whole point of classifying errors is that we need to know something about the error in question - in arrar it's the last error that wasn't an annotation; in errgo, the concept is made more explicit. > I think what I really want is Roger's errgo package, but with the Diagnosis > code removed, and just expose underlying errors through the Cause() method. As an example of the motivation for the Diagnosis concept, and the kind of thing I'm wanting to guard against, here's a hypothetical example. We want to make a network connection, but need to read a configuration file first: http://play.golang.org/p/zXqLB9sb_e Consider some of the possible errors from ReadPacketFromNetwork, (each preceded by the error that will be used if we call arrar.Check on the error): *os.PathError cannot read configuration file: open /home/.conf: no such file or directory io.ErrUnexpectedEOF cannot read configuration file: cannot read address: unexpected EOF *net.OpError cannot dial "foo.com": dial tcp 12.34.56.78 connection timed out io.ErrUnexpectedEOF network read failed: unexpected EOF Note that io.ErrUnexpectedEOF can be returned as the result of two very different operations. Suppose we have a package that's using the above code, and we observe that sometimes a server is faulty and delivers truncated data. We might well implement something like this: http://play.golang.org/p/NNqryXwz95 But that would be faulty code, in a very subtle and easy to overlook way, because it will do the wrong thing if the configuration file happens to be truncated. By contrast, here's how the two pieces of code would look using errgo: http://play.golang.org/p/uaTjyO7_se http://play.golang.org/p/Ze_YTCcfzm Note that, even ignoring the doc comment, it is easy to tell by looking at the high level function, ReadPacketFromNetwork, what classifiable errors might be returned from it. This makes it easy to write the doc comment - the contract with the caller is clear. This, then, is the essence of the distinction between Diagnosis and Cause - the Diagnosis is part of the contract of a function - it is something that a caller may depend on and that it is the responsibility of a function to maintain. The Cause is just some arbitrary underlying error - there are many possible error causes, but we don't want arbitrary calling code to become dependent on them, because every error cause reflects some implementation detail, and depending on implementation details makes the code base more brittle and harder to change. William correctly pointed out that this does impose a burden on us - if we do want to allow another kind of error diagnosis to be returned from a function, we are obliged to change all the intermediate paths to pass it through. My hope is that this will not actually prove much of a problem in practice, and that it will be much outweighed by being able to *know* that no caller can be depending on the unmentioned diagnoses and the resulting freedom to change code. We currently have some extremely subtle dependencies between distantly related pieces of code, and I would really like to make those dependencies evident. This doesn't mean that we should do all of that immediately, but it's a direction that I think would be very healthy for the code base overall. > I'd like to then steal arrar's function for returning a list of the > underlying errors (GetErrorStack), and I like his string formatting better > for the stack-trace-lite (DetailedErrorStack), but I wouldn't bother with > formatting parameter, because I honestly will never care. I deliberately made the error info formatting fit on a single line so it would work well in log files, but it would be easy to implement an alternative format too. > However, I also was an honest-to-god stacktrace available from the root > error, because both implementations can miss important information about how > you got to the root error that a real stacktrace wouldn't miss. I'd be interested to see an example of when you think the errgo/arrar approach would not produce sufficient information (assuming that errors are Traced/Wrapped by default). The honest-to-god stack trace is unfortunately insufficient in a concurrent world where errors may be (and often are) transferred between goroutines. cheers, rog. -- Juju-dev mailing list Juju-dev@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/juju-dev