Re: Sets of IOErrors?
On 29-Sep-1999, Marcin 'Qrczak' Kowalczyk [EMAIL PROTECTED] wrote: I only don't like the details about IOError. It's not extensible and Dynamic is still not nice. I have no idea how to make it better. Probably some general way of making extensible datatypes. Could you explain what you don't like about Dynamic? -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Sets of IOErrors?
On 08-Sep-1999, Alastair Reid [EMAIL PROTECTED] wrote: I recently had occasion to write this function: -- do a, if that fails do b, if that fails, raise b's exception (?) :: IO a - IO a - IO a a ? b = a `catch` \_ - b Simple enough, but if b fails, I don't really want to raise b's exception, I want to raise both a's exception and b's exception. Yes, that kind of thing is not that uncommon. BTW, you didn't explain why you need to raise both exceptions. The reason is to correctly handle the case where programs test the type of the exception raised and attempt some corrective action accordingly. For example, a program which is attempting to access a network might have a high-level exception handler that checks for network I/O errors and tries to handle them by restarting the network connection and then retrying. If a network I/O error occurs, and then a lower-level handler tries some alternative means of recovery, and that alternative throws some other kind of exception, it's important that the exception thrown to the higher-level handler still include the information that a network I/O error occurred, so that the higher-level handler will restart the network connection. The C++ standardization committee considered the issue of multiple exceptions, and basically decided that it was too difficult to deal with. If a C++ exception handler itself throws an exception, then the default behaviour is to abort the program. You can install a special handler to deal with such situtations, but that special handler can't throw an exception, so there's not much it can safely do other than to abort. In theory it's possible to program around that kind of thing in C++, but in practice this is exceedingly difficult (for various reasons, including problems related to memory management and the lack of a clone() member in the std::exception class), and furthermore requires agreement between the different parts of the program about how sets of exceptions are represented, so in practice nobody does it, as far as I am aware. It would be nice if Haskell could handle that problem better than C++. Automatic memory management and the ease of writing higher-order combinators such as `?' above mean that it is already much easier in Haskell. However, some kind of standard library support is also essential if library packages written by different authors are to cooperate. So I agree with the essence of Alistair Reid's suggestions. However, I have some quibbles about the details. What I'd like (in some future version of Haskell) is an IOError constructor which lets me merge two IOErrors together and appropriate operations to test for it and, perhaps, take it apart: IO.multipleErrors :: [IOError] - IOError IO.isMultipleErrors :: IOError - Bool IO.ioeGetErrors :: IOError - [IOError] The intention is that an IOError is a non-empty list of atomic errors (ie any of the existing IOErrors). Thus "multipleErrors" is essentially concat, ioeGetErrors is (essentially) the identity function (it returns a list of IOErrors which are guaranteed to be non-empty) and all existing functions for testing IOErrors are modified to be defined only over singleton lists. What existing functions for testing IOErrors? Apart from the Eq and Show classes, Haskell 98 doesn't define any, AFAIK. If you're suggesting that Eq or Show be undefined for IOErrors that represent sets of exceptions, then I would have to disagree... Perhaps you meant the Hugs/ghc functions for testing Exceptions. If so, the above suggestions should be revised to refer to Exceptions rather than IOErrors. The exception handling in Haskell 98 needs substantial work, IMHO. IMHO Haskell 200X should pick up the Hugs/ghc extensions related to exception handling, or something along those lines, in particular: (a) allow throwing exceptions from arbitrary code, not just from within the IO monad (b) allow throwing and catching of dynamically typed values, e.g. using an interface like the Hugs/ghc Dynamic library Once you have (b), then supporting sets of exceptions is straight-forward. However, to handle the multiple exceptions problem, support for sets of exceptions still needs to be part of the standard library, to ensure that applications and library packages cooperate in this respect. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Sets of IOErrors?
On 28-Sep-1999, Alastair Reid [EMAIL PROTECTED] wrote: Fergus Henderson [EMAIL PROTECTED] replied: What existing functions for testing IOErrors? Apart from the Eq and Show classes, Haskell 98 doesn't define any, AFAIK. The IO library defines these functions. (http://haskell.cs.yale.edu/onlinelibrary/io.html) isAlreadyExistsError :: IOError - Bool isDoesNotExistError :: IOError - Bool isAlreadyInUseError :: IOError - Bool isFullError :: IOError - Bool isEOFError:: IOError - Bool isIllegalOperation:: IOError - Bool isPermissionError :: IOError - Bool isUserError :: IOError - Bool ioeGetErrorString :: IOError - String ioeGetHandle :: IOError - Maybe Handle ioeGetFileName:: IOError - Maybe FilePath Oh, of course. Thanks for the correction. (I searched the Haskell 98 Report but I forgot to search the Haskell 98 Library Report.) ... I was going to suggest that we redefine isAlreadyExistsError and friends to return true if the associated error is a member of the set. But if we do this, what do we do with ioeGetErrorString and friends? Deprecate them. Define them to return the value associated with the first matching exception, but deprecate them, and instead provide alternatives that work with sets of exceptions. (b) allow throwing and catching of dynamically typed values, e.g. using an interface like the Hugs/ghc Dynamic library Despite having written a good deal of the Hugs-GHC Dynamic library[2], I'm wary of making it part of the standard because it is limited to handling monomorphic values only: no polymorphism, no overloading. I'm not entirely certain that I understand what you mean by "no polymorphism, no overloading". If you want to put a polymorphic overloaded function into a Dynamic, you can just put it in a wrapper data structure and put the wrapper into a Dynamic, and then you can get that wrapper out of the Dynamic and then get the polymorphic overloaded function out of the wrapper. (This presumes that the language supports first-class polymorphism in data structures, but ghc supports that, and I think it is likely that Haskell 200X will.) I presume, then, that what you are talking about the difficulty of putting a monomorphically typed value into a Dynamic and then later extracting it with a polymophic or overloaded type. Extracting values with polymorphic types can be solved using existential types. This technique is used in the Mercury standard library (versions rotd-1998-11-19 and greater). Using this technique can be a little cumbersome, but it gets the job done. It does require a small extension to the current Dynamic interface, specifically a function analagous to the `univ_value' function in the Mercury standard library, but that extension would be trivial to implement once you have existential types. Extracting values with overloaded types requires what I called "dynamic type class casts". I agree that the lack of support for this is a problematic limitation. So, if a better solution comes along before Haskell 200X is finalized, that would be great. But failing that, I think something like the Hugs-GHC Dynamic library is certainly much better than using a non-extensible Exception type, even if does not solve all the problems that we would like it to solve. I'm also a little leery of the lack of structure that would result from using dynamic typing to provide extensibility. The hierarchial organisation of exceptions in Java and other OO languages seems to be a good match to the task. Well, C++ and Java certainly use dynamic typing in their exception handling. The difference is that those languages support the equivalent of dynamic type class casts, whereas the Hugs/ghc Dynamic library does not. Certainly it would be nicer to impose more structure. And if/when Haskell gets support for type class casts or something equivalent, then as you say we can do that using Haskell's type class system. But if dynamic type class casts or something with similar functionality don't arrive in time for Haskell 200X, then we will need an interim solution. Currently the only alternative is to encode all exception types into strings. Using Dynamic could hardly be worse. If the choice is between strings and Dynamic, then I'll choose Dynamic every time. Note that Ada 95 used strings, and so the Ada 95 experts in comp.lang.ada recommend using serialization techniques to encode arbitrary data types in strings for the purposes of exception handling. I surely hope that Haskell 200X experts don't have to recommend the same advice. (Though, again, how does this interact with having sets of errors?) I don't think that is a problem for hierarchical classification. You just have to apply the dynamic type class cast (or equivalent) to every member of the set and collect the set of results. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW:
Re: Sets of IOErrors?
On 28-Sep-1999, Alastair Reid [EMAIL PROTECTED] wrote: (b) allow throwing and catching of dynamically typed values, e.g. using an interface like the Hugs/ghc Dynamic library [discussion of Dynamic library, etc deleted] [The following is a bit of a straw-man: it doesn't quite work but may have good parts which can be used in other designs.] (part of) Another approach is to extend Haskell with extensible datatypes as is done in ML. ... If Haskell supported extensible datatypes, it would be easy to define a hierarchy of exception values. ... Disadvantages of this approach include: o Most Haskell features can be described as "just syntactic sugar" - it's hard to do this here. o It's hard to write total functions over extensible datatypes (eg try writing a Show function for the attached definition of IOError). o This is the only compelling use for extensible datatypes - wouldn't it be better to support exception handling more directly? Another disadvantage is that with this approach you miss out on multiple inheritance. Your example categorization IOError Win32Error GDIError BadRegion BadBrush PosixError ENOTDIR ENAMETOOLONG EINTR UserError AlreadyExists categorizes errors according to the subsystem that produced them (Win32, Posix), but often you want to categorize errors differently, and some errors may fall into multiple categories. Note that using `Dynamic', you could easily implement the same kind of single-inheritance hierarchical categorization as in your post, using nested Dynamic values rather than nested extensible types. But with the appropriate language extension for dynamic type class casts, Dynamic could also be used for single- or multiple-inheritance hierarchies based on type classes rather than nesting, whereas I don't see any way to extend the extensible type approach to support multiple-inheritance hierarchies. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Sets of IOErrors?
On 28-Sep-1999, George Russell [EMAIL PROTECTED] wrote: I must be missing something. Isn't it blindlingly obvious that here SML's exception mechanism is streets ahead of Haskell's? Please, what am I missing? Is there some lurking type-unsafeness? SML's exception mechanism essentially forces sequential execution, and (I think) causes problems for equational reasoning. Haskell's exception mechanism preserves referential transparency and gives the compiler more freedom to reorder and/or parallelize code. [snip] (a) allow throwing exceptions from arbitrary code, not just from within the IO monad Aaarrgh no. If you're going to do unsafe operations then be honest about it and use IOExts.unsafePerformIO. Throwing an exception from arbitrary code is no less safe than calling `error' or executing a case for which no pattern matches. Allowing exceptions to be thrown from arbitrary code does not violate referential transparency. See the paper that Alastair Reid cited. Actually I don't often feel the need to do this (I don't think there's any example in UniForM). Exceptions tend to be either because of something IOish (file not found), in which case you are already doing state, or else because of a bug in the program, in which case "error" is probably appropriate. There are many common examples where exceptions arise for reasons other than I/O, for example integer overflow, division by zero, taking the square root of a negative number, head of an empty list, and so forth. If we want to be able to write robust programs, then we need to be able to recover from such errors. Likewise, if we want to be able to write efficient programs, then we need to be able to recover from such errors, because often it is more efficient and/or easier to make the common case fast and to deal with the rare cases via an exception handler than to write code which avoids such raising such errors/exceptions in the first place. I suppose the main problem with "error" is that theoretically you have to crash out of all enclosing Haskell code. I don't understand enough about the semantics of haskell to know the answer to this question, but could you provide a way of wrapping up a value a - Maybe a so that if somewhere during the evaluation of the a, there is an attempt to evaluate "error", the result is Nothing (and perhaps an error message printed on the screen), otherwise Just (something). Would this break the semantics? Unfortunately yes. The possibility of infinite loops causes trouble here. For the details, which are rather subtle, see the paper. But a function of type `a - IO (Maybe a)' need not suffer from the same problem. You can safely throw exceptions from anywhere, so long as you only catch them from code in the IO Monad. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Sets of IOErrors?
Wed, 29 Sep 1999 11:43:06 +0200, George Russell [EMAIL PROTECTED] pisze: When that happens in my code it counts as a bug! Therefore error is appropriate. If you are in some larger Haskell universe calling component Haskell code it is unfortunate if a single error calls the entire universe to collapse, so you do need some way of recovering, I agree to both of the above. I only don't like the details about IOError. It's not extensible and Dynamic is still not nice. I have no idea how to make it better. Probably some general way of making extensible datatypes. but perhaps a function as mentioned of type a - IO (Maybe a) which traps the error might suffice for this case. How would it work with lazy evaluation? I guess it would simply be strict in its argument, but what if `a' is a list and an error occurs during computation of some further element? Probably it would just fail with _|_. BTW, can there make sense a universal variant of seq which recursively seqs components of datatypes? `x == x' would sometimes work in practice. But maybe it does not make sense theoretically: does `const [0..]' _contain_ the list so it would be seqed? Maybe the only sensible generic seq just evaluates the top constructor. But that way (Bool,Bool) is not "isomorphic" to a four-valued enumeration wrt seq effect. There are strictness annotations in datatypes, I think they create isomorphic types, but they create _new_ types: a list should not be strict all the times but it could make sense to catch the errors inside the elements when needed. Hmm, a class SuperSeq, probably automatically derivable? I'm confused. And I'm not sure if it would be useful at all. -- __("Marcin Kowalczyk * [EMAIL PROTECTED] http://kki.net.pl/qrczak/ \__/ GCS/M d- s+:-- a22 C++$ UL++$ P+++ L++$ E- ^^W++ N+++ o? K? w(---) O? M- V? PS-- PE++ Y? PGP-+ t QRCZAK 5? X- R tv-- b+++ DI D- G+ e h! r--%++ y-
Re: Sets of IOErrors?
Thu, 30 Sep 1999 02:08:23 +1000, Fergus Henderson [EMAIL PROTECTED] pisze: I only don't like the details about IOError. It's not extensible and Dynamic is still not nice. I have no idea how to make it better. Probably some general way of making extensible datatypes. Could you explain what you don't like about Dynamic? The implementation relies on mkTyCon used in the top level definition; no referential transparency. Fixing it would begin to rely on manual invention of unique names. This could fail when the same type name is used in different modules. One must know the exact type inside to do anything with it. One cannot make a singleton list of the value inside Dynamic and put the result into Dynamic without knowing its type. One cannot declare that all values in a particular Dynamic are in some class and apply methods of that class to them without knowing their types. Errors in describing the types will result in dangerous behavior of using some value after unsafeCoercing it to an incorrect type. One cannot describe more elaborate type constructs, like local universal quantification. Fortunately they may always be put into separately named datatype, it's not that bad. At last, there is this explicit Typeable class. Making its instance is a non-creative technical issue. Disclaimer: I have not used Dynamic in practice. Maybe it is more useful than ugly. But I think that most uses of Dynamic are symptoms of some lacking capabilities in the type system. -- __("Marcin Kowalczyk * [EMAIL PROTECTED] http://kki.net.pl/qrczak/ \__/ GCS/M d- s+:-- a22 C++$ UL++$ P+++ L++$ E- ^^W++ N+++ o? K? w(---) O? M- V? PS-- PE++ Y? PGP-+ t QRCZAK 5? X- R tv-- b+++ DI D- G+ e h! r--%++ y-
Re: Sets of IOErrors?
Fergus Henderson wrote: [snip] So why limit expressiveness by providing only the former? Why indeed? You are right. I hadn't realised that a - IO (Maybe a) would still suffer from non-determinism. (Because if you have x = error "foo" + _|_ it may cause a return of Nothing or else no return at all.) So there is no reason for not going the whole way and letting the caller inspect the contents of the exception.
Re: Sets of IOErrors?
[ Apologies that this message is not about Haskell, but it is about interesting use of language! ] Amazingly, Perec's book has been translated into English, by Gilbert Adair, completely maintaining it's "e-less" constraint! The title of the English translation is "A Void" (the French title would translate as "The Disappearance", but that contains e's). It's a fantastic book! I highly recommend it. Nikhil -Original Message- From: Jan Skibinski [mailto:[EMAIL PROTECTED]] Sent: Wednesday, September 29, 1999 10:06 AM Cc: [EMAIL PROTECTED] Subject: Re: Sets of IOErrors? ... Regardless of all sorts of constraints, Haskell has achieved a lot in its current form. Working with restrictions can be fun and intellectually stimulating but nevertheless it can be sometimes frustrating. It's like writing a book without using one of the letters of the alphabet, which can be really challenging if you choose not to use the most frequent vowel, as "e" in English. (*) ... (*) ... - An excentric novel "La disparition" by French writer Georges Perec, written in French (again without letter "e") in 1969. This is a 26-volumed book (corresponding to the fact that there are 26 letters of alphabet), with volume 5 missing.
Re: Sets of IOErrors?
On 08-Sep-1999, Alastair Reid [EMAIL PROTECTED] wrote: What I'd like (in some future version of Haskell) is an IOError constructor which lets me merge two IOErrors together and appropriate operations to test for it and, perhaps, take it apart: Fergus Henderson [EMAIL PROTECTED] replied: What existing functions for testing IOErrors? Apart from the Eq and Show classes, Haskell 98 doesn't define any, AFAIK. The IO library defines these functions. (http://haskell.cs.yale.edu/onlinelibrary/io.html) isAlreadyExistsError :: IOError - Bool isDoesNotExistError :: IOError - Bool isAlreadyInUseError :: IOError - Bool isFullError :: IOError - Bool isEOFError:: IOError - Bool isIllegalOperation:: IOError - Bool isPermissionError :: IOError - Bool isUserError :: IOError - Bool ioeGetErrorString :: IOError - String ioeGetHandle :: IOError - Maybe Handle ioeGetFileName:: IOError - Maybe FilePath The idea of these functions is to make use of the type abstract and, hence, simplify extension of the type. (I'm not sure it achieves this because existing code that carefully checks for the above errors would probably break if we added a new kind of error.) If you're suggesting that Eq or Show be undefined for IOErrors that represent sets of exceptions, then I would have to disagree... No problem with Show - I use it all the time. I think having and using Eq is ok (though it defeats whatever abstraction the above operations provide). Obviously we'd want to add an "elem"-like function as well if we have sets of exceptions. I was going to suggest that we redefine isAlreadyExistsError and friends to return true if the associated error is a member of the set. But if we do this, what do we do with ioeGetErrorString and friends? The exception handling in Haskell 98 needs substantial work, IMHO. I'd certainly agree with that. My suggestion was meant as a minimal change to what is already there. IMHO Haskell 200X should pick up the Hugs/ghc extensions related to exception handling, or something along those lines, in particular: (a) allow throwing exceptions from arbitrary code, not just from within the IO monad Fergus and I are in complete agreement here - but then we are coauthors on a paper[1] that describes how to do it :-) I think this is quite a minor change - but very, very important if you're trying to do real things with Haskell. (b) allow throwing and catching of dynamically typed values, e.g. using an interface like the Hugs/ghc Dynamic library Despite having written a good deal of the Hugs-GHC Dynamic library[2], I'm wary of making it part of the standard because it is limited to handling monomorphic values only: no polymorphism, no overloading. I'm also a little leery of the lack of structure that would result from using dynamic typing to provide extensibility. The hierarchial organisation of exceptions in Java and other OO languages seems to be a good match to the task. (Though, again, how does this interact with having sets of errors?) It is probably possible to encode such a hierarchy using Haskell's type classes but I worry that the lack of overloading in the present Dynamic library would prevent us from combining the two. (Anyone care to prove me wrong on this claim?) -- Alastair Reid[EMAIL PROTECTED]http://www2.cs.utah.edu/~reid/ [1] http://www2.cs.utah.edu/~reid/except-pldi.ps.gz @inproceedings{ReidA:PLDI99 ,author="S.L. {Peyton Jones} and A. Reid and Tony Hoare and Simon Marlow and Fergus Henderson" ,title="A semantics for imprecise exceptions" ,booktitle = "Programming Languages Design and Implementation (PLDI'99)" ,organization = "ACM press" ,year = "1999" ,month ="May" ,pages="25-36" ,abstract=" Some modern superscalar microprocessors provide only imprecise exceptions. That is, they do not guarantee to report the same exception that would be encountered by a straightforward sequential execution of the program. In exchange, the offer increased performance or decreased area (which amount to much the same thing). This performance/precision tradeoff has not so far been explored at the programming langauge level. In this paper we propose a design for imprecise exceptions in the lazy functional programming language Haskell. We discuss various simpler designs, and conclude that imprecision is essential if the language is still to enjoy its current rich algebra of transformations. We sketch a precise semantics for the language extended with exceptions. From the functional programming point of view, the paper shows how to extend Haskell with exceptions without crippling the language or its compilers. From the point of view of the wider programming language community, we pose the question of whether precision and performance can be traded off in other languages too. " } [2] http://haskell.cs.yale.edu/ghc/docs/latest/libraries/libs.html
Re: Sets of IOErrors?
(b) allow throwing and catching of dynamically typed values, e.g. using an interface like the Hugs/ghc Dynamic library [discussion of Dynamic library, etc deleted] [The following is a bit of a straw-man: it doesn't quite work but may have good parts which can be used in other designs.] (part of) Another approach is to extend Haskell with extensible datatypes as is done in ML. This is what I did in the late, unlamented GreenCard 1 - you could define a new IOError constructor whenever you wanted. This was easy to do because GreenCard 1's implementation exploited the fact that it had full access to Hugs' internal data structures. When we moved onto GreenCard 2 and had to add GHC support, this was no longer such an easy choice and we reluctantly switched to encoding errors as strings. If Haskell supported extensible datatypes, it would be easy to define a hierarchy of exception values. For example, the attached pseudocode creates a hierarchy like this: IOError Win32Error GDIError BadRegion BadBrush PosixError ENOTDIR ENAMETOOLONG EINTR UserError AlreadyExists Disadvantages of this approach include: o Most Haskell features can be described as "just syntactic sugar" - it's hard to do this here. o It's hard to write total functions over extensible datatypes (eg try writing a Show function for the attached definition of IOError). o This is the only compelling use for extensible datatypes - wouldn't it be better to support exception handling more directly? -- Alastair Reid[EMAIL PROTECTED]http://www2.cs.utah.edu/~reid/ module IO(...) where ... -- define the type extensible IOError :: * -- define some constructors constructor UserError :: String - IOError constructor AlreadyExists :: FilePath - IOError module Posix(...) where -- Posix Stuff import IO(IOError) -- define a new hierarchy of errors extensible PosixError :: * -- link the new hierarchy into IOError constructor PosixError :: PosixError - IOError -- define some Posix errors constructor ENOTDIR :: FilePath - PosixError constructor ENAMETOOLONG :: FilePath - PosixError constructor EINTR:: PosixError ... module Win32(...) where -- Windows 95/98/NT stuff import IO(IOError) -- define a new hierarchy of errors extensible Win32Error :: * -- link the new hierarchy into IOError constructor Win32Error :: Win32Error - IOError module Win32GDI(...) where -- Windows graphics primitives import Win32(Win32Error) -- define a new hierarchy of errors extensible GDIError :: * -- link the new hierarchy into IOError constructor GDIError :: GDIError - Win32Error -- define some GDI errors constructor BadRegion :: HREGION - GDIError constructor BadBrush :: HBRUSH - GDIError ...