On Tue, Dec 4, 2012 at 3:56 PM, Matthew Flatt <mfl...@cs.utah.edu> wrote: > I'm not sure... > > Providing error-code information is an appealing idea. Instead of > `exn:fail:errno', we'd probably add a `prop:exn:error-code' property > that is implemented by subtypes of `exn:fail:filesystem', etc. Also, > the value of the property would need to provide both a code and a > "kind" for the code, such as 'posix for a code from from `errno' or > 'windows for a code from GetLastError(). The way that error codes are > currently included in the text of error messages could be leveraged to > get them into an exception structure without too many changes. > > I'm not sure about `errno->symbol' and how to make it both portable and > general, though. I can imagine an `errno->symbol' that works with the > most common and standard error results, but I think there must be some > platform variation, to say nothing of Windows error code versus Posix > `errno' values. Does some other high-level language deal with error > codes in a good way that we could imitate?
This is a good point. I was implicitly following python here, which, to its credit, exposes details from the platform fairly directly, and which, to its detriment, sometimes lacks a clear "platform independent" abstraction. > > > Then again, I worry that dealing with error codes in a general way is a > lot of work for a feature that probably won't find much use. That is, I > actually disagree with your claim that "it is often desirable to > distinguish error causes with as fine a resolution as the platform > provides"; it seems fairly rare to me --- aside from including > information in error messages, which Racket already does. Hm, I'm not sure what you mean by "a general way". Exposing the underlying operating system data to discriminate errors should just be about some factor times N work, where N is the number of platforms racket supports, right? OTOH, creating rich cross-platform exception hierarchies *would* be a lot of work. > > We have mostly dealt with interesting error cases by making new > exception subtypes, such as `exn:fail:filesystem:exists'. It might be > that `exn:fail:filesystem:not-exists' is useful to add. If that's the > only immediate need, though, maybe we should just add that for now. > Ok, so creating a rich cross-platform exception hierarchy *does* sound like a lot of work, and I agree that it's best to only enrich the hierarchy when there's a pressing need. However, without exposing the underlying details to application programmers, *some* applications are left with hacks like parsing strerror output. I still advocate for exposing the platform specific error codes making it clear that those details are not cross-platform and leaving it up to the application developer. Maybe I'm biased from my experience, but I've often worked on or maintained python applications where a given application will initially be deployed on a single platform, so general abstractions are not strictly necessary. Here's my understanding of the trade-offs, where I anticipate the "application effort" which is the effort for *new* applications which need some distinction between errors, versus "backwards compatibility" which is the effort required by pre-existing applications which do not care about these distinctions: Approach 1: Do nothing. Racket effort: 0 Application effort: Moderate / Messy - parse exception messages strings, accounting for locale and platform issues for the application's target audience. (messy, laborious) Backwards Compatibility: perfect - No work necessary for other applications. Approach 2: Expose underlying error values directly and unambiguously without any abstraction. Racket effort: small (? I'm guessing here) Application effort: Moderate / Slightly cleaner - Create conditional error handlers depending on the platform and specific error cases needed by the application. Backwards Compatibility: I'm not sure, but foresee 3 possibilities: - Perfect compatibility; The error values are retrievable in a manner that doesn't affect any structures or pre-existing apis. (For example a get-current-errno function.) - Case-specific incompatibility; Some structures have new fields, only code that matches/manipulates those structures need to be changed. Approach 3: Enrich the exception hierarchy in a cross-platform, but more specific way Racket effort: large Application effort: Small / Clean - Applications have the perfect distinction in the error hierarchy for their use case and it's mapped correctly to the target platforms Backwards Compatibility: Two options: - Fair: The existing hierarchy is not modified, only new branches are added. - Poor: The existing hierarchy is modified; many existing applications must be altered. Does this seem accurate? I favor approach 2 because it's minimal work for racket, and could be done in a manner that's perfectly or nearly backwards compatible. It exposes non-portable values with some api, so the documentation should just clearly indicate this. This gives applications which really need some distinction a clean starting point, though they have to manage the platform issues. Actually, I haven't yet learned at all about the ffi, so it could be there's an even less disruptive option: Make ffi modules for various platforms that extract error information, to be used inside exception handlers. This would require no modifications to racket, but would give some specialized applications the tool they need. > > At Mon, 3 Dec 2012 14:37:36 -0800, Nathan wrote: >> Hello, >> >> I'm new to racket, and only mildly familiar with scheme, so I >> apologize if I'm missing important details. >> >> Whenever an error occurs related to a C interface which uses "errno" >> to signal the kind of error, I propose the associated racket exception >> should capture this errno value separately from the error message. I >> propose exposing the literal value and also a cross-platform way to >> determine the symbolic name for that value on the current platform. >> >> The reason is that it is often desirable to distinguish error causes >> with as fine a resolution as the platform provides. The specific >> example that lead me to this issue is that I want to read a file, but, >> in the case that the file is not present, return a default value. In >> any other case (such as incorrect permissions, or should the path >> refer to a directory instead of a file), I want the error to >> propagate. A work-around for my particular case is to check for the >> file's existence before opening it for reading, but notice that this >> introduces one more race-condition, so I prefer my original strategy. >> This is just a single case, and I expect in general it will be useful >> or necessary to distinguish errno values. >> >> I chatted with dyoo on the freenode irc channel and he suggested I >> send mail to this list, because this would be a backwards incompatible >> change. >> >> I'm not too familiar with racket, so there may be much better API >> designs, but my first idea was to add a new base exception struct >> along the lines of (struct exn:fail:errno exn:fail (errno)) and some >> separate module for translation with errno->symbol and symbol->errno. >> Maybe it's nicer to just include the symbol directly in the exception >> structure. >> >> There are a few implementation details I'd caution about: >> >> In general, "errno" may be a macro, so a simple ffi wrapper may not be >> cross-platform (according to the manpage). The value of errno is in >> thread-local storage, and I have no idea how that relates to racket >> thread safety. >> >> Including *only* the symbolic name of an errno would be a mistake, >> IMO, because I'm an anti-fan of abstractions hiding the truth. OTOH, >> I'm also a fan of abstractions and interfaces, so excluding the >> symbolic name is essential for writing clean cross-platform code. >> >> >> Regards, >> Nathan Wilcox >> >> ps: So far I'm really enjoying racket! The documentation is >> excellent; there's a debian package I can simply apt-get; and I've >> been playing with the custom language design features which are much >> nicer than I realized for scheme-likes. I had always thought the >> status quo was splicing with quasiquote so the syntax features are a >> wonderful surprise. Also, I'm very interested to explore the >> sandboxing features, but I haven't yet learned them in detail. I'm >> quite pleased they include time and memory constraints. >> _________________________ >> Racket Developers list: >> http://lists.racket-lang.org/dev Regards, Nathan Wilcox _________________________ Racket Developers list: http://lists.racket-lang.org/dev