Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Mon, Mar 30, 2009 at 11:03 PM, Henning Thielemann wrote: > > On Sun, 29 Mar 2009, John Lato wrote: > >> On Sat, Mar 28, 2009 at 9:49 PM, Henning Thielemann >> wrote: >>> >>> On Sat, 28 Mar 2009, John Lato wrote: >>> Honestly, me neither, until recently. I'm only barely starting to understand it, and I do think there's a great deal of overlap. Even if an error is a bug that can be fixed by the programmer, certain exceptional situations can also be fixed by the programmer by handling the exception, even if they can't be detected in advance. >>> >>> For example? >> >> A file not being written because of a permissions error. This can't >> be detected in advance due to effects from other processes, but it's a >> predictable enough exception that the programmer should handle it for >> IO. > > Indeed. However I still do not see the overlap of errors and exceptions. :-( Really? You wrote in a prior email: >>> Btw. not handling an exception is an error. An exception thrown at run-time reveals a programming error, and if the exception could not be thrown in this context then the error wouldn't exist either. The error is that the programmer did not deal with an exception. Conceptually I think the difference between errors and exceptions is clear, but many programming errors are a result of a lack or improper handling of exceptions. That's the overlap to which I refer. > Handling of exceptions may mean that you print a message and abort the > affected operation, that is you often do not need a specific exception > handling. E.g. if the user wants to load a file into an editor, and the file > is read-protected, then the editor reports, that the file cannot be loaded > and does not load it. The editor should keep running, in contrast to when it > detects a programming error. The same should happen when the editor > encounters a memory overflow. So I cannot follow the argument of "memory > exhaustion shouldn't happen anymore today, so there is no need to handle > it". I remember this was claimed in this thread, too. However, recovering > from a memory exhaustion can be tricky. Can the garbage collector still free > memory, when it cannot allocate memory temporarily? I thought the argument was that "when memory exhaustion occurs in a Haskell application, it is (usually) the result of a programming error." This means handling the exception and trying to continue work is not the proper approach to dealing with memory exhaustion. >>> Btw. not handling an exception is an error. >> >> Agreed generally. But some exceptions are likely in given contexts, >> others are not. I don't think it's necessary to handle every possible >> exception, just the ones that are likely and predictable for a given >> activity. > > Since handling of exceptions often consist of reporting the exception and > aborting an operation, it should always be managable to handle all possible > exceptions. Manageable, but possibly not useful for all functions. I don't believe that anyone's advocating that all IO functions are wrapped in a 'try' or 'bracket', so long as there are exception handlers at appropriate levels. John ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sun, 29 Mar 2009, John Lato wrote: On Sat, Mar 28, 2009 at 9:49 PM, Henning Thielemann wrote: On Sat, 28 Mar 2009, John Lato wrote: Honestly, me neither, until recently. I'm only barely starting to understand it, and I do think there's a great deal of overlap. Even if an error is a bug that can be fixed by the programmer, certain exceptional situations can also be fixed by the programmer by handling the exception, even if they can't be detected in advance. For example? A file not being written because of a permissions error. This can't be detected in advance due to effects from other processes, but it's a predictable enough exception that the programmer should handle it for IO. Indeed. However I still do not see the overlap of errors and exceptions. :-( Handling of exceptions may mean that you print a message and abort the affected operation, that is you often do not need a specific exception handling. E.g. if the user wants to load a file into an editor, and the file is read-protected, then the editor reports, that the file cannot be loaded and does not load it. The editor should keep running, in contrast to when it detects a programming error. The same should happen when the editor encounters a memory overflow. So I cannot follow the argument of "memory exhaustion shouldn't happen anymore today, so there is no need to handle it". I remember this was claimed in this thread, too. However, recovering from a memory exhaustion can be tricky. Can the garbage collector still free memory, when it cannot allocate memory temporarily? Btw. not handling an exception is an error. Agreed generally. But some exceptions are likely in given contexts, others are not. I don't think it's necessary to handle every possible exception, just the ones that are likely and predictable for a given activity. Since handling of exceptions often consist of reporting the exception and aborting an operation, it should always be managable to handle all possible exceptions. Excluding generic "The impossible happened, file a bug report" handlers. This is the one special case, where program errors are catched in order to allow debugging.___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Mon, 30 Mar 2009, Simon Marlow wrote: Nicolas Pouillard wrote: By reading the documentation of 'hGetLine' [1] one can see that this function can throw only an EOF exception so why not give it a type like below? hGetLine :: Handle -> ErrorT EOF IO String Since one will have to handle the error case it would be better to treat only the possible cases, no? I'm afraid the documentation is incomplete. hGetLine can also fail because e.g. the device it was reading from has been unplugged, or it was reading from a network filesystem and the network has gone down. And there might be yet more errors to be invented in the future, so I'm not sure it would be a good idea to reflect this level of detail in the type. Or the file could get read-protected. In principle I like to show the possible exceptions by types, but the point Simon raises is also important. I think IOError is good here and one must use the IOError analysis functions, to find out whether one can do something more specific in the case. For the default case there should be good possibilities to report the exception, e.g. translate it to the user language. For example when a file was not found, one can tell the user how to obtain it. But if the file is read-protected, a default message would suffice. The type IOError would still exclude an exception like "Parser failure". Thus explicit IOError is more descriptive than the implicit exceptions in IO monad today. It's still interesting how to handle sets of exceptions. If you use the exception type (Either ParserException IOError) then this is different from (Either IOError ParserException). I think we should use type classes for exceptions. Then you can use one type containing all exceptions in an application, but the type signature tells which exceptions are actually possible. E.g. parser :: ParserException e => ExceptionalT e ParserMonad a getLine :: IOException e => ExceptionalT e IO String fileParser :: (ParserException e, IOException e) => ExceptionalT e IO String ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sat, 2009-03-28 at 01:27 +0100, Henning Thielemann wrote: > Jonathan Cast schrieb: > > >> i.e., that application's > >> file decoding result should be an Either type that anticipates that > >> the file encoding may be invalid. > > > > This is pretty standard, I thought. Do people write Haskell file input > > methods that are undefined (`throw exceptions') on invalid inputs (e.g., > > do people use read to parse input from users or the file system[1])? > > With > > case reads str of > [(x, "")] -> Just x > _ -> Nothing > > you are safe. (I think it's now available as maybeRead.) Hmm, hoogle doesn't know that name. > In general, relying on a well-formed input file is an error. However, if > your program detects a format error in file input, it could throw an > exception. But this means that your program must be prepared for these > problems. Right. > >> I will also guess if the file is unreadable because of an external > >> I/O problem like no read access to file or filesystem, you would > >> similarly expect this to be treated like that - I mean, ideally, e.g., > >> hGetLine :: Handle -> IO (Either IOError String) > > > > IO is an exception monad already. I don't think there's an objection to > > throwing exceptions with throwIO and catching them in IO; my objection, > > at least, is to designing your program to throw exceptions from > > (ostensibly...) *pure* code and catch those in IO, in a live > > environment. > > Actually, I really object to have exception handling built into IO > monad. Especially with extensible-exceptions package you can hide which > kinds of exceptions can occur in a piece of code, which is a bad thing. > When it comes to lazy I/O, which is problematic in itself, it is better > to have explicit exceptions (i.e. IO (Either IOError String)) on top of > exception-free IO. See the recent thread on safe lazy I/O: >http://www.haskell.org/pipermail/haskell-cafe/2009-March/058205.html Yi's new parsing library (just finished the paper a couple days ago) seems quite appropriate to a lazy IO library; for that library, partial grammars are errors anyway, so the issue doesn't really arise. If you want IO failure/parsing failure handling in lazy IO, my preference would be for a separate failure-handling hook (which can throw an asynchronous exception if needed) rather than for any kind of synchronous exception mechanism per se. There's really not much you can do, except tell the user either `hey, that doesn't look like valid input!' or `sorry, the other side of the network connection disappeared', and hope the operator can correct the issue. I don't think it's really important to let the input processing (as opposed to parsing) code handle the situation specifically. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Fri, 2009-03-27 at 21:16 -0700, Donn Cave wrote: > Quoth Henning Thielemann , > > On Fri, 27 Mar 2009, Donn Cave wrote: > > > >> Quoth Jonathan Cast , > >> > >>> An `error' is any condition where the correct response is for the > >>> programmer to change the source code :) > >> > >> That's a broad category, that overlaps with conditions where there > >> are one or more correct responses for the original program as well. > >> > >> If I throw exceptions within the type system, using IO or whatever, > >> and at some later time observe that I have caught one that would > >> have been better handled closer to its source, for example. I've > >> already technically satisfied my requirement, since everything is > >> in an exception monad, but the exception is still a bug. > > > > I don't understand this one. > > A lame attempt to demonstrate that "condition where [a] correct > response is to change the code" Please don't mis-quote me. I said `the' correct response. Both programming and operating computers are goal-directed processes; an error is a situation where the program detects a bug such that it cannot make progress toward the current goal without the programmer going and fixing that bug. If you have a condition where there is something (useful...) you want to do within the context of the current source code, do not use an error to signal that condition. Use an exception. > applies to too many cases to be > useful. (And that there are no cases where [the only] correct > response is to change the code.) I think Henning's response, and others, have adequately covered this. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
Nicolas Pouillard wrote: Excerpts from Henning Thielemann's message of Sat Mar 28 21:49:33 +0100 2009: On Sat, 28 Mar 2009, John Lato wrote: From: Donn Cave I have never felt that I really understood that one. Honestly, me neither, until recently. I'm only barely starting to understand it, and I do think there's a great deal of overlap. Even if an error is a bug that can be fixed by the programmer, certain exceptional situations can also be fixed by the programmer by handling the exception, even if they can't be detected in advance. For example? Btw. not handling an exception is an error. I will also guess if the file is unreadable because of an external I/O problem like no read access to file or filesystem, you would similarly expect this to be treated like that - I mean, ideally, e.g., hGetLine :: Handle -> IO (Either IOError String) Not necessarily, but possibly. The big difference, of course, is that decoding can be a pure operation, while reading never is. I personally wouldn't mind if hGetLine had the type you give. The way I see it, there are two advantages to exceptions in this case. The first is that it's very easy for exceptions to trickle up and be handled at a higher level. The second 'advantage' is that the programmer doesn't need to explicitly handle exceptions, whereas an Either would require at least a pattern match to use the resulting value. I'm afraid there is some confusion about what we mean with "exception". Do you only mean the thing that is silently handled in the IO monad? Is Left in Either an exception for you, too? In explicit-exception I call the corresponding constructor Exception, because that's what it is used for. I like to call all those things exceptions, because they are intended for the same purpose: Signalling exceptional situations that we cannot avoid in advance but that must be handled when they occur. You can use IO and its exceptions, I call them IO exceptions. It does not show in its types that and which exceptions can occur. Some people consider this an advantage, I consider this an disadvantage. You can use error codes or Either or even better Exceptional from the explicit-exception package, and Haskell is strong enough to treat these like exceptions in C++/Java/Modula-3 etc. because you can use their monad transformer variants ErrorT and ExceptionalT respectively. Those monad transformers allow automatical termination of a series of actions once an exceptional result is obtained. But since ErrorT and ExceptionalT are burned into the types, you cannot miss to handle them. So the most convenient type for hGetLine would be hGetLine :: Handle -> ErrorT IOError IO String By reading the documentation of 'hGetLine' [1] one can see that this function can throw only an EOF exception so why not give it a type like below? hGetLine :: Handle -> ErrorT EOF IO String Since one will have to handle the error case it would be better to treat only the possible cases, no? I'm afraid the documentation is incomplete. hGetLine can also fail because e.g. the device it was reading from has been unplugged, or it was reading from a network filesystem and the network has gone down. And there might be yet more errors to be invented in the future, so I'm not sure it would be a good idea to reflect this level of detail in the type. Cheers, Simon ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
Henning Thielemann wrote: > Actually, I really object to have exception handling built into IO > monad. > I couldn't agree more. If I want to write non-recovering code, I can always just say (Right foo) <- readLine , and hope that the RTS is smart enough to print the error message in the left constructor should that match fail. -- (c) this sig last receiving data processing entity. Inspect headers for copyright history. All rights reserved. Copying, hiring, renting, performance and/or quoting of this signature prohibited. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sat, 2009-03-28 at 12:51 +0300, Gregory Petrosyan wrote: > On Sat, Mar 28, 2009 at 10:53 AM, Ketil Malde wrote: > > So the difference between an exception or an error type is mainly what > > you intend to do about it. There's no point in wrapping divisions in > > Maybe unless you actually are able to do something useful to recover > > from a zero denominator. > > That is exactly the point I was trying to make. > > When I write a code, I can't say in advance, in what way it will be used. > So, for dealing with errors, I have to choose one way or another, mostly > without that knowledge. When I'm using e.g. C++, it's easy: > something like mantra "when in doubt, throw an exception" :-) > combined with RAII, works good (but not ideal, of course). > > So, I'll ask again: when I program in Haskell, what mechanism should I use? If you don't know, use a (true) exception. That is, Left or Exception or throwIO. Only use error or throw when you *know* the condition is un-recoverable. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sat, Mar 28, 2009 at 9:49 PM, Henning Thielemann wrote: > > On Sat, 28 Mar 2009, John Lato wrote: > >>> From: Donn Cave >>> >>> I have never felt that I really understood that one. >> >> Honestly, me neither, until recently. I'm only barely starting to >> understand it, and I do think there's a great deal of overlap. Even >> if an error is a bug that can be fixed by the programmer, certain >> exceptional situations can also be fixed by the programmer by handling >> the exception, even if they can't be detected in advance. > > For example? A file not being written because of a permissions error. This can't be detected in advance due to effects from other processes, but it's a predictable enough exception that the programmer should handle it for IO. Handling a DivByZero exception when doing IO, however, is very likely wrong. > > Btw. not handling an exception is an error. Agreed generally. But some exceptions are likely in given contexts, others are not. I don't think it's necessary to handle every possible exception, just the ones that are likely and predictable for a given activity. Excluding generic "The impossible happened, file a bug report" handlers. > >>> I will also guess if the file is unreadable because of an external >>> I/O problem like no read access to file or filesystem, you would >>> similarly expect this to be treated like that - I mean, ideally, e.g., >>> hGetLine :: Handle -> IO (Either IOError String) >> >> Not necessarily, but possibly. The big difference, of course, is that >> decoding can be a pure operation, while reading never is. >> >> I personally wouldn't mind if hGetLine had the type you give. The way >> I see it, there are two advantages to exceptions in this case. The >> first is that it's very easy for exceptions to trickle up and be >> handled at a higher level. The second 'advantage' is that the >> programmer doesn't need to explicitly handle exceptions, whereas an >> Either would require at least a pattern match to use the resulting >> value. > > I'm afraid there is some confusion about what we mean with "exception". Do > you only mean the thing that is silently handled in the IO monad? Yes. I was comparing exceptions as they exist in IO in Haskell to the proposed hGetLine type. > Is Left in > Either an exception for you, too? No. > In explicit-exception I call the > corresponding constructor Exception, because that's what it is used for. > I like to call all those things exceptions, because they are intended for > the same purpose: Signalling exceptional situations that we cannot avoid in > advance but that must be handled when they occur. You can use IO and its > exceptions, I call them IO exceptions. It does not show in its types that > and which exceptions can occur. Some people consider this an advantage, I > consider this an disadvantage. I remain undecided on this for the moment. I should take another look at explicit-exception now that I understand its intent better. John ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Excerpts from Henning Thielemann's message of Sat Mar 28 21:49:33 +0100 2009: > > On Sat, 28 Mar 2009, John Lato wrote: > > >> From: Donn Cave > >> > >> I have never felt that I really understood that one. > > > > Honestly, me neither, until recently. I'm only barely starting to > > understand it, and I do think there's a great deal of overlap. Even > > if an error is a bug that can be fixed by the programmer, certain > > exceptional situations can also be fixed by the programmer by handling > > the exception, even if they can't be detected in advance. > > For example? > > Btw. not handling an exception is an error. > > >> I will also guess if the file is unreadable because of an external > >> I/O problem like no read access to file or filesystem, you would > >> similarly expect this to be treated like that - I mean, ideally, e.g., > >> hGetLine :: Handle -> IO (Either IOError String) > > > > Not necessarily, but possibly. The big difference, of course, is that > > decoding can be a pure operation, while reading never is. > > > > I personally wouldn't mind if hGetLine had the type you give. The way > > I see it, there are two advantages to exceptions in this case. The > > first is that it's very easy for exceptions to trickle up and be > > handled at a higher level. The second 'advantage' is that the > > programmer doesn't need to explicitly handle exceptions, whereas an > > Either would require at least a pattern match to use the resulting > > value. > > I'm afraid there is some confusion about what we mean with "exception". > Do you only mean the thing that is silently handled in the IO monad? Is > Left in Either an exception for you, too? In explicit-exception I call the > corresponding constructor Exception, because that's what it is used for. > I like to call all those things exceptions, because they are intended for > the same purpose: Signalling exceptional situations that we cannot avoid > in advance but that must be handled when they occur. You can use IO and > its exceptions, I call them IO exceptions. It does not show in its types > that and which exceptions can occur. Some people consider this an > advantage, I consider this an disadvantage. You can use error codes or > Either or even better Exceptional from the explicit-exception package, and > Haskell is strong enough to treat these like exceptions in > C++/Java/Modula-3 etc. because you can use their monad transformer > variants ErrorT and ExceptionalT respectively. Those monad transformers > allow automatical termination of a series of actions once an exceptional > result is obtained. But since ErrorT and ExceptionalT are burned into the > types, you cannot miss to handle them. > So the most convenient type for hGetLine would be > hGetLine :: Handle -> ErrorT IOError IO String By reading the documentation of 'hGetLine' [1] one can see that this function can throw only an EOF exception so why not give it a type like below? hGetLine :: Handle -> ErrorT EOF IO String Since one will have to handle the error case it would be better to treat only the possible cases, no? [1]: http://www.haskell.org/ghc/docs/latest/html/libraries/base/System-IO.html#v%3AhGetLine -- Nicolas Pouillard ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sat, 28 Mar 2009, John Lato wrote: From: Donn Cave I have never felt that I really understood that one. Honestly, me neither, until recently. I'm only barely starting to understand it, and I do think there's a great deal of overlap. Even if an error is a bug that can be fixed by the programmer, certain exceptional situations can also be fixed by the programmer by handling the exception, even if they can't be detected in advance. For example? Btw. not handling an exception is an error. I will also guess if the file is unreadable because of an external I/O problem like no read access to file or filesystem, you would similarly expect this to be treated like that - I mean, ideally, e.g., hGetLine :: Handle -> IO (Either IOError String) Not necessarily, but possibly. The big difference, of course, is that decoding can be a pure operation, while reading never is. I personally wouldn't mind if hGetLine had the type you give. The way I see it, there are two advantages to exceptions in this case. The first is that it's very easy for exceptions to trickle up and be handled at a higher level. The second 'advantage' is that the programmer doesn't need to explicitly handle exceptions, whereas an Either would require at least a pattern match to use the resulting value. I'm afraid there is some confusion about what we mean with "exception". Do you only mean the thing that is silently handled in the IO monad? Is Left in Either an exception for you, too? In explicit-exception I call the corresponding constructor Exception, because that's what it is used for. I like to call all those things exceptions, because they are intended for the same purpose: Signalling exceptional situations that we cannot avoid in advance but that must be handled when they occur. You can use IO and its exceptions, I call them IO exceptions. It does not show in its types that and which exceptions can occur. Some people consider this an advantage, I consider this an disadvantage. You can use error codes or Either or even better Exceptional from the explicit-exception package, and Haskell is strong enough to treat these like exceptions in C++/Java/Modula-3 etc. because you can use their monad transformer variants ErrorT and ExceptionalT respectively. Those monad transformers allow automatical termination of a series of actions once an exceptional result is obtained. But since ErrorT and ExceptionalT are burned into the types, you cannot miss to handle them. So the most convenient type for hGetLine would be hGetLine :: Handle -> ErrorT IOError IO String ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sat, 28 Mar 2009, Gregory Petrosyan wrote: On Sat, Mar 28, 2009 at 10:53 AM, Ketil Malde wrote: So the difference between an exception or an error type is mainly what you intend to do about it. There's no point in wrapping divisions in Maybe unless you actually are able to do something useful to recover from a zero denominator. That is exactly the point I was trying to make. When I write a code, I can't say in advance, in what way it will be used. So, for dealing with errors, I have to choose one way or another, mostly without that knowledge. When I'm using e.g. C++, it's easy: something like mantra "when in doubt, throw an exception" :-) combined with RAII, works good (but not ideal, of course). In C++ you can either throw exceptions or return integer codes in order to show exceptional situations. An error is, if you write in non-allocated memory areas. So, I'll ask again: when I program in Haskell, what mechanism should I use? I think this question was enough answered in general here in the thread and on the Wiki pages. I think we can better discuss concrete situations. Do you have a function in your code where you are uncertain whether to use error or exceptions? ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Sat, 28 Mar 2009, Ketil Malde wrote: Jonathan Cast writes: i.e., that application's file decoding result should be an Either type that anticipates that the file encoding may be invalid. This is pretty standard, I thought. Do people write Haskell file input methods that are undefined (`throw exceptions') on invalid inputs (e.g., do people use read to parse input from users or the file system[1])? I often write parsers that either run successfully, or abort with an exception. I could of course return an Either type, but that would mean the whole file would need to be parsed before any results could be returned at all - which is a showstopper for streaming processing of large files. If you need a lazy parser with error reporting, I suggest Control.Monad.Exception.Asynchronous from the explicit-exception package. Since at least my files are typically machine generated, a parse error is either a programmer error in my parser, a programmer error in the generating program, or an operator error (viz. the user running the program on a completely different file type). That's no excuse. You can never rely on proper file formatting since the user alter the file while you process it, delete it, or remove the disk it is stored on. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
On Sat, Mar 28, 2009 at 10:53 AM, Ketil Malde wrote: > So the difference between an exception or an error type is mainly what > you intend to do about it. There's no point in wrapping divisions in > Maybe unless you actually are able to do something useful to recover > from a zero denominator. That is exactly the point I was trying to make. When I write a code, I can't say in advance, in what way it will be used. So, for dealing with errors, I have to choose one way or another, mostly without that knowledge. When I'm using e.g. C++, it's easy: something like mantra "when in doubt, throw an exception" :-) combined with RAII, works good (but not ideal, of course). So, I'll ask again: when I program in Haskell, what mechanism should I use? Gregory ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Jonathan Cast writes: >> i.e., that application's file decoding result should be an Either >> type that anticipates that the file encoding may be invalid. > This is pretty standard, I thought. Do people write Haskell file input > methods that are undefined (`throw exceptions') on invalid inputs (e.g., > do people use read to parse input from users or the file system[1])? I often write parsers that either run successfully, or abort with an exception. I could of course return an Either type, but that would mean the whole file would need to be parsed before any results could be returned at all - which is a showstopper for streaming processing of large files. Since at least my files are typically machine generated, a parse error is either a programmer error in my parser, a programmer error in the generating program, or an operator error (viz. the user running the program on a completely different file type). In any case, I want the program execution to halt and report the error as soon as possible. So the difference between an exception or an error type is mainly what you intend to do about it. There's no point in wrapping divisions in Maybe unless you actually are able to do something useful to recover from a zero denominator. -k -- If I haven't seen further, it is by standing in the footprints of giants ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Quoth Henning Thielemann , > On Fri, 27 Mar 2009, Donn Cave wrote: > >> Quoth Jonathan Cast , >> >>> An `error' is any condition where the correct response is for the >>> programmer to change the source code :) >> >> That's a broad category, that overlaps with conditions where there >> are one or more correct responses for the original program as well. >> >> If I throw exceptions within the type system, using IO or whatever, >> and at some later time observe that I have caught one that would >> have been better handled closer to its source, for example. I've >> already technically satisfied my requirement, since everything is >> in an exception monad, but the exception is still a bug. > > I don't understand this one. A lame attempt to demonstrate that "condition where [a] correct response is to change the code" applies to too many cases to be useful. (And that there are no cases where [the only] correct response is to change the code.) >> Or if I catch an non-IO error using Control.Exception.catch (if I >> were using ghc), and take advantage of the opportunity to release >> locks, post various notices, etc. - I evidently have a bug, but I >> also may need to have a programmed response for it. > > The usual example against clear separation of exceptions and errors is the > web server which catches 'error's in order to keep running. However, the > web server starts its parts as threads, and whenever one thread runs into > an 'error', it is terminated, just like an external shell program, that > terminates with a segmentation fault. So, yes an error might be turned > into an exception, but these are rare cases. In general it is hard or > impossible to correctly clean up after an error, because the error occured > due to something that you as programmer didn't respect. The "error > handler" could well make things worse by freeing memory that is already > deallocated and so on. But you'd have to weigh that against the consequences of not continuing. I can certainly see the attraction of the exception monad treatment for anticipated errors. It might tend to obscure a central computation for the sake of handling a lot of weird little errors that may never actually be encountered, and doesn't it occasionally have a problem with making things strict that didn't already need to be? but at least it makes it easier to reason about the code in terms that include errors. And I'm using nhc98 lately, so no Control.Exception.catch for me, but if I had it, I'd use it, just like I wear a helmet when I ride my bicycle or motorcycle. Donn ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Henning Thielemann wrote: > The usual example against clear separation of exceptions and errors is the > web server which catches 'error's in order to keep running. > However, the web server starts its parts as threads, and whenever one thread > runs into an 'error', it is terminated, just like an external shell > program, that terminates with a segmentation fault. So, yes an error might be > turned into an exception, but these are rare cases. In > general it is hard or impossible to correctly clean up after an error, > because the error occured due to something that you as programmer > didn't respect. The "error handler" could well make things worse by freeing > memory that is already deallocated and so on. I don't see that as an argument against 'clear separation', really. Having _some_ way of dealing an error (from within a program), in special circumstances doesn't preclude clearly separating how it's done from exception handling. I always find it jarring when an HUnit test I've run tells me it encountered an 'exception', when I'm testing pure code (nevertheless I'd also find it annoying if the entire test run terminated because of a failed pattern match). With respect to the last point - isn't proving that a given program can't corrupt its own RTS possible, even in the presence of errors? ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
> > Message: 8 > Date: Fri, 27 Mar 2009 10:41:05 -0700 > From: Jonathan Cast > Subject: Re: [Haskell-cafe] Re: Exception handling in numeric > computations > To: Gregory Petrosyan > Cc: haskell-cafe@haskell.org > Message-ID: <1238175665.20367.12.ca...@jcchost> > Content-Type: text/plain; charset=utf-8 > > On Fri, 2009-03-27 at 20:38 +0300, Gregory Petrosyan wrote: >> On Fri, Mar 27, 2009 at 7:31 PM, Donn Cave wrote: >> > Quoth John Lato , >> > >> >> An exception is caused by some sort of interaction with the run-time >> >> system (frequently a hardware issue). The programmer typically can't >> >> check for these in advance, but can only attempt to recover after >> >> they've happened. >> >> >> >> An error is some sort of bug that should be fixed by the programmer. >> > >> > I have never felt that I really understood that one. >> >> Me too :-) >> >> BTW, John, how often do you encounter _hardware_ issues compared to "errors"? > > Can't speak for anyone else, but I usually encounter hardware issues > just before I replace the hardware... This is getting confusing with two "John"'s replying. For myself, I do not encounter hardware issues often at all (unless you count buggy drivers). They are the exceptional condition. > >> Is an "out of memory" thing an error or exception? >> You will say "exception, for sure", wouldn't you? :-) > > No. GHC possesses an out-of-memory exception that (IIRC) it never > throws, because it's simply not worth trying to recover from heap > exhaustion. Maybe 20 years ago it was, but these days a program that > manages to exhaust space is almost certainly either buggy or poorly > optimized. I would have said exception, although I agree with the assessment that it's not worth trying to recover from. > > An `error' is any condition where the correct response is for the > programmer to change the source code :) > >> And if it is a >> result of applying >> known-to-be-very-memory-hungry algorithms to non-trivial input? Looks like >> programmer's error, doesn't it? > > See above. Due to their brevity, I sometimes find it difficult to understand Jonathan's replies. So at the risk of putting words in his mouth, I will take an attempt to explain this further. Keep in mind these are my words, not his, and it's possible I have completely misunderstood his point since I've only been using Haskell intensively for about a year (and I have no formal CS training, and other biographical points apply as well). Running out of memory is an exceptional condition. Doing something that is likely to cause memory exhaustion is a programming error, because it was the wrong solution to the problem. Memory exhaustion, the exceptional condition, is an end result of making the programming error. In this case (but not all), it is possible to treat the symptoms by handling the exception, but the general solution is to avoid the problem in the first place. > >> And I think I can provide lots of similar examples. >> >> If there exists separation between errors and exceptions, it should be >> very strong >> and evident — otherwise "casual programmers" like myself will need to >> stare at the >> ceiling every time they write something to decide what suits best. > I am not sure when I would every throw an exception in Haskell code, short of re-raising from a handler. Things that I would have used exceptions for in C-sharp can be handled explicitly with Maybe or Either, a style that I greatly prefer. John > Protip: try pacing instead of staring at the ceiling. > > jcc > ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
> From: Donn Cave > Quoth John Lato , > >> An exception is caused by some sort of interaction with the run-time >> system (frequently a hardware issue). The programmer typically can't >> check for these in advance, but can only attempt to recover after >> they've happened. >> >> An error is some sort of bug that should be fixed by the programmer. > > I have never felt that I really understood that one. Honestly, me neither, until recently. I'm only barely starting to understand it, and I do think there's a great deal of overlap. Even if an error is a bug that can be fixed by the programmer, certain exceptional situations can also be fixed by the programmer by handling the exception, even if they can't be detected in advance. In general I would say that I agree with JCC's responses. > > What about invalid inputs? Say someone encounters a disk full error, > and the resulting partly written file is now unreadable data for its > intended application because of an invalid file encoding? Is that > an exception, or a bug that should be fixed? > > My guess is that you'll say it's a bug, i.e., that application's > file decoding result should be an Either type that anticipates that > the file encoding may be invalid. Not necessarily. I would be satisfied with a Maybe :) I've encountered malformed data frequently enough that any decoders I write (and I have done so recently) would anticipate invalid data. > > I will also guess if the file is unreadable because of an external > I/O problem like no read access to file or filesystem, you would > similarly expect this to be treated like that - I mean, ideally, e.g., > hGetLine :: Handle -> IO (Either IOError String) Not necessarily, but possibly. The big difference, of course, is that decoding can be a pure operation, while reading never is. I personally wouldn't mind if hGetLine had the type you give. The way I see it, there are two advantages to exceptions in this case. The first is that it's very easy for exceptions to trickle up and be handled at a higher level. The second 'advantage' is that the programmer doesn't need to explicitly handle exceptions, whereas an Either would require at least a pattern match to use the resulting value. John ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Fri, 27 Mar 2009, Donn Cave wrote: Quoth Jonathan Cast , An `error' is any condition where the correct response is for the programmer to change the source code :) That's a broad category, that overlaps with conditions where there are one or more correct responses for the original program as well. If I throw exceptions within the type system, using IO or whatever, and at some later time observe that I have caught one that would have been better handled closer to its source, for example. I've already technically satisfied my requirement, since everything is in an exception monad, but the exception is still a bug. I don't understand this one. Or if I catch an non-IO error using Control.Exception.catch (if I were using ghc), and take advantage of the opportunity to release locks, post various notices, etc. - I evidently have a bug, but I also may need to have a programmed response for it. The usual example against clear separation of exceptions and errors is the web server which catches 'error's in order to keep running. However, the web server starts its parts as threads, and whenever one thread runs into an 'error', it is terminated, just like an external shell program, that terminates with a segmentation fault. So, yes an error might be turned into an exception, but these are rare cases. In general it is hard or impossible to correctly clean up after an error, because the error occured due to something that you as programmer didn't respect. The "error handler" could well make things worse by freeing memory that is already deallocated and so on. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Thu, 26 Mar 2009, John Lato wrote: Henning T., FYI your constant advocacy has gotten at least one person around to this view. I'm glad that it is not (only) perceived as annoyance. :-) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Jonathan Cast schrieb: >> i.e., that application's >> file decoding result should be an Either type that anticipates that >> the file encoding may be invalid. > > This is pretty standard, I thought. Do people write Haskell file input > methods that are undefined (`throw exceptions') on invalid inputs (e.g., > do people use read to parse input from users or the file system[1])? With case reads str of [(x, "")] -> Just x _ -> Nothing you are safe. (I think it's now available as maybeRead.) In general, relying on a well-formed input file is an error. However, if your program detects a format error in file input, it could throw an exception. But this means that your program must be prepared for these problems. >> I will also guess if the file is unreadable because of an external >> I/O problem like no read access to file or filesystem, you would >> similarly expect this to be treated like that - I mean, ideally, e.g., >> hGetLine :: Handle -> IO (Either IOError String) > > IO is an exception monad already. I don't think there's an objection to > throwing exceptions with throwIO and catching them in IO; my objection, > at least, is to designing your program to throw exceptions from > (ostensibly...) *pure* code and catch those in IO, in a live > environment. Actually, I really object to have exception handling built into IO monad. Especially with extensible-exceptions package you can hide which kinds of exceptions can occur in a piece of code, which is a bad thing. When it comes to lazy I/O, which is problematic in itself, it is better to have explicit exceptions (i.e. IO (Either IOError String)) on top of exception-free IO. See the recent thread on safe lazy I/O: http://www.haskell.org/pipermail/haskell-cafe/2009-March/058205.html ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Quoth Jonathan Cast , > An `error' is any condition where the correct response is for the > programmer to change the source code :) That's a broad category, that overlaps with conditions where there are one or more correct responses for the original program as well. If I throw exceptions within the type system, using IO or whatever, and at some later time observe that I have caught one that would have been better handled closer to its source, for example. I've already technically satisfied my requirement, since everything is in an exception monad, but the exception is still a bug. Or if I catch an non-IO error using Control.Exception.catch (if I were using ghc), and take advantage of the opportunity to release locks, post various notices, etc. - I evidently have a bug, but I also may need to have a programmed response for it. Donn ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Fri, 2009-03-27 at 20:38 +0300, Gregory Petrosyan wrote: > On Fri, Mar 27, 2009 at 7:31 PM, Donn Cave wrote: > > Quoth John Lato , > > > >> An exception is caused by some sort of interaction with the run-time > >> system (frequently a hardware issue). The programmer typically can't > >> check for these in advance, but can only attempt to recover after > >> they've happened. > >> > >> An error is some sort of bug that should be fixed by the programmer. > > > > I have never felt that I really understood that one. > > Me too :-) > > BTW, John, how often do you encounter _hardware_ issues compared to "errors"? Can't speak for anyone else, but I usually encounter hardware issues just before I replace the hardware... > Is an "out of memory" thing an error or exception? > You will say "exception, for sure", wouldn't you? :-) No. GHC possesses an out-of-memory exception that (IIRC) it never throws, because it's simply not worth trying to recover from heap exhaustion. Maybe 20 years ago it was, but these days a program that manages to exhaust space is almost certainly either buggy or poorly optimized. An `error' is any condition where the correct response is for the programmer to change the source code :) > And if it is a > result of applying > known-to-be-very-memory-hungry algorithms to non-trivial input? Looks like > programmer's error, doesn't it? See above. > And I think I can provide lots of similar examples. > > If there exists separation between errors and exceptions, it should be > very strong > and evident — otherwise "casual programmers" like myself will need to > stare at the > ceiling every time they write something to decide what suits best. Protip: try pacing instead of staring at the ceiling. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Fri, Mar 27, 2009 at 7:31 PM, Donn Cave wrote: > Quoth John Lato , > >> An exception is caused by some sort of interaction with the run-time >> system (frequently a hardware issue). The programmer typically can't >> check for these in advance, but can only attempt to recover after >> they've happened. >> >> An error is some sort of bug that should be fixed by the programmer. > > I have never felt that I really understood that one. Me too :-) BTW, John, how often do you encounter _hardware_ issues compared to "errors"? Is an "out of memory" thing an error or exception? You will say "exception, for sure", wouldn't you? :-) And if it is a result of applying known-to-be-very-memory-hungry algorithms to non-trivial input? Looks like programmer's error, doesn't it? And I think I can provide lots of similar examples. If there exists separation between errors and exceptions, it should be very strong and evident — otherwise "casual programmers" like myself will need to stare at the ceiling every time they write something to decide what suits best. Gregory ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Fri, 2009-03-27 at 09:31 -0700, Donn Cave wrote: > Quoth John Lato , > > > An exception is caused by some sort of interaction with the run-time > > system (frequently a hardware issue). The programmer typically can't > > check for these in advance, but can only attempt to recover after > > they've happened. > > > > An error is some sort of bug that should be fixed by the programmer. > > I have never felt that I really understood that one. > > What about invalid inputs? Say someone encounters a disk full error, > and the resulting partly written file is now unreadable data for its > intended application because of an invalid file encoding? Is that > an exception, or a bug that should be fixed? NB: Of course it's a bug: if the disk is full, the partially written file should be discarded and the previous version retained. I'm not going to hold you accountable for Unix's bugs, though. > My guess is that you'll say it's a bug, I think you mean `exception' here. > i.e., that application's > file decoding result should be an Either type that anticipates that > the file encoding may be invalid. This is pretty standard, I thought. Do people write Haskell file input methods that are undefined (`throw exceptions') on invalid inputs (e.g., do people use read to parse input from users or the file system[1])? > I will also guess if the file is unreadable because of an external > I/O problem like no read access to file or filesystem, you would > similarly expect this to be treated like that - I mean, ideally, e.g., > hGetLine :: Handle -> IO (Either IOError String) IO is an exception monad already. I don't think there's an objection to throwing exceptions with throwIO and catching them in IO; my objection, at least, is to designing your program to throw exceptions from (ostensibly...) *pure* code and catch those in IO, in a live environment. > Does that make sense so far? jcc [1] This post should not be taken as an endorsement of the use of the Read class for any purpose, nor as an endorsement of its continued existence in the standard library. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Quoth John Lato , > An exception is caused by some sort of interaction with the run-time > system (frequently a hardware issue). The programmer typically can't > check for these in advance, but can only attempt to recover after > they've happened. > > An error is some sort of bug that should be fixed by the programmer. I have never felt that I really understood that one. What about invalid inputs? Say someone encounters a disk full error, and the resulting partly written file is now unreadable data for its intended application because of an invalid file encoding? Is that an exception, or a bug that should be fixed? My guess is that you'll say it's a bug, i.e., that application's file decoding result should be an Either type that anticipates that the file encoding may be invalid. I will also guess if the file is unreadable because of an external I/O problem like no read access to file or filesystem, you would similarly expect this to be treated like that - I mean, ideally, e.g., hGetLine :: Handle -> IO (Either IOError String) Does that make sense so far? Donn ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
On Fri, 2009-03-27 at 12:24 +, Chris Kuklewicz wrote: > Jonathan Cast wrote: > > Sure. Which also points out that the original safeDiv wasn't actually > > safe, since there's no guarantee of what evaluate will do with x and y. > > (Actually, there's not much guarantee of what evaluate does anyway --- > > just that any errors in e's definition get turned into exceptions by the > > time evaluate e finishes running, or don't turn into exceptions at all). > > > > That is not true if you mean "any errors" as "any and all errors". No, that's not what I mean. I just couldn't think of a good phrasing for `errors that would prevent e from being evaluated to HNF'. Which is still a lousy phrasing. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Fri, Mar 27, 2009 at 10:01 AM, Gregory Petrosyan wrote: > Thanks a lot for the answer! > > On Thu, Mar 26, 2009 at 4:36 PM, John Lato wrote: >> Languages with checked exceptions usually use them for two purposes: >> >> 1. Exceptional conditions - disk full, CPU on fire, etc. >> 2. Error handling - invalid arguments to a function, attempt to >> invert non-invertible matrix, etc. > > Is there any good rule someone can use to decide whether it is error > or exception? > For me, this is the most important thing, because IMHO you (as library > writer) often can't > say what is it, it's up to client of your code to decide. An exception is caused by some sort of interaction with the run-time system (frequently a hardware issue). The programmer typically can't check for these in advance, but can only attempt to recover after they've happened. An error is some sort of bug that should be fixed by the programmer. There is some overlap for certain cases, notably divide by 0. Dividing by 0 is an error, because it's something that the program should never do, and it can be detected and dealt with by the programmer in advance. However most systems allow the divide function to be called with a 0 denominator. The function has a precondition, meaning the onus is on the programmer to not do it, however this is not enforced by the language. If a program does this in error, the result is an exception because the result is not a valid number (this is codified with NaN for IEEE floats). In this case, a programming error results in an exception. The proper solution is to fix the source of the problem, the error, instead of trying to clean up the results. > >> Henning T., FYI your constant advocacy has gotten at least one person >> around to this view. > > Can you please provide me some links about error/exception separation? http://haskell.org/haskellwiki/Error http://haskell.org/haskellwiki/Exception ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
Jonathan Cast wrote: Sure. Which also points out that the original safeDiv wasn't actually safe, since there's no guarantee of what evaluate will do with x and y. (Actually, there's not much guarantee of what evaluate does anyway --- just that any errors in e's definition get turned into exceptions by the time evaluate e finishes running, or don't turn into exceptions at all). That is not true if you mean "any errors" as "any and all errors". The 'evaluate' operation only forces the argument into weak head normal form (WHNF) not normal form (NF). Thus 'evaluate' may succeed and then its return value could be examined further and only then trigger an exception. I could easily define a new "Integral" type where WHNF is not NF and demonstrate the problem. The solution is to use 'evaluate' only on known primitive types like Int, or on polymorphic data constrained to be NFDATA and use the 'rnf' strategy within the call to 'evaluate'. -- Chris ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Thanks a lot for the answer! On Thu, Mar 26, 2009 at 4:36 PM, John Lato wrote: > Languages with checked exceptions usually use them for two purposes: > > 1. Exceptional conditions - disk full, CPU on fire, etc. > 2. Error handling - invalid arguments to a function, attempt to > invert non-invertible matrix, etc. Is there any good rule someone can use to decide whether it is error or exception? For me, this is the most important thing, because IMHO you (as library writer) often can't say what is it, it's up to client of your code to decide. > Henning T., FYI your constant advocacy has gotten at least one person > around to this view. Can you please provide me some links about error/exception separation? Gregory ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Thu, 2009-03-26 at 21:57 -0400, wren ng thornton wrote: > Jonathan Cast wrote: > > Xiao-Yong Jin wrote: > > > > Xiao-Yong Jin wrote: > > > > > So I have another question. Is the following function safe > > > > > and legitimate? > > > > > > > > > >> safeDiv :: (Exception e, Integral a) => > > > > >>a -> a -> Either e a > > > > >> safeDiv x y = unsafePerformIO . try . evaluate $ div x y > > > > > >> safeDiv' :: (Exception e, Integral a) => > > >> a -> a -> Either e a > > >> safeDiv' _ 0 = Left e > > >> safeDiv' x y = Right $ div x y > > > > [...] > > Other than that, I think the imprecise exceptions paper guarantees that > > these two functions are equivalent (albeit unwisely: see below). > > I don't think so. The evaluation of x and y may throw errors before we > get around to div. Sure. Which also points out that the original safeDiv wasn't actually safe, since there's no guarantee of what evaluate will do with x and y. (Actually, there's not much guarantee of what evaluate does anyway --- just that any errors in e's definition get turned into exceptions by the time evaluate e finishes running, or don't turn into exceptions at all). > * safeDiv' will evaluate y (to pattern match against 0) and may return > an error, e, whereas safeDiv will return Left e if div is strict in y. > > * safeDiv' postpones evaluating x and so may return Right e, whereas > safeDiv will return Left e if div is strict in x. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Jonathan Cast wrote: Xiao-Yong Jin wrote: > > Xiao-Yong Jin wrote: > > > So I have another question. Is the following function safe > > > and legitimate? > > > > > >> safeDiv :: (Exception e, Integral a) => > > >>a -> a -> Either e a > > >> safeDiv x y = unsafePerformIO . try . evaluate $ div x y > >> safeDiv' :: (Exception e, Integral a) => >> a -> a -> Either e a >> safeDiv' _ 0 = Left e >> safeDiv' x y = Right $ div x y [...] Other than that, I think the imprecise exceptions paper guarantees that these two functions are equivalent (albeit unwisely: see below). I don't think so. The evaluation of x and y may throw errors before we get around to div. * safeDiv' will evaluate y (to pattern match against 0) and may return an error, e, whereas safeDiv will return Left e if div is strict in y. * safeDiv' postpones evaluating x and so may return Right e, whereas safeDiv will return Left e if div is strict in x. -- Live well, ~wren ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
safeDiv :: (Exception e, Integral a) => a -> a -> Either e a safeDiv x y = unsafePerformIO . try . evaluate $ div x y I just want to know, from a theoretical point of view, whether this 'safeDiv' in above definition is the same as safeDiv' :: (Exception e, Integral a) => a -> a -> Either e a safeDiv' _ 0 = Left e safeDiv' x y = Right $ div x y No. Firstly, safeDiv' doesn't compile!-) Then, if you replace 'e' by 'DivideByZero' and adjust the types: *Main> safeDiv 1 (throw Overflow) Left arithmetic overflow *Main> safeDiv' 1 (throw Overflow) *** Exception: arithmetic overflow Try ':info ArithException' for more in the same group. You could use other functions in Control.Exceptions to get more control about which exceptions you want to handle and how, but so far, there is no indication that 'unsafePerformIO' is the right hammer to use here.. Claus -- unsagePerformIO: some things are just not wise to do ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Thu, 2009-03-26 at 14:23 -0400, Xiao-Yong Jin wrote: > Henning Thielemann writes: > > > On Thu, 26 Mar 2009, Xiao-Yong Jin wrote: > > > >> So I have another question. Is the following function safe > >> and legitimate? > >> > >>> safeDiv :: (Exception e, Integral a) => > >>>a -> a -> Either e a > >>> safeDiv x y = unsafePerformIO . try . evaluate $ div x y > >> > >> I believe it should be okay to use this 'safeDiv'. What do > > > > I think that question is wrong way around. The real question is, why > > do you want to solve your problem using unsafePerformIO? > > I just want to know, from a theoretical point of view, > whether this 'safeDiv' in above definition is the same as > > > safeDiv' :: (Exception e, Integral a) => > > a -> a -> Either e a > > safeDiv' _ 0 = Left e > > safeDiv' x y = Right $ div x y You need some sort of type case here to make sure your first case matches only if e is the right type for divide-by-zero errors (too lazy to look it up atm). Alternatively, you could replace your type variable e with the actual exception type you want, here and in the unsafePerformIO version. Other than that, I think the imprecise exceptions paper guarantees that these two functions are equivalent (albeit unwisely: see below). > For the question why do I want to do that, I am not sure. I > guess if the function which has an error call inside is > provided by other library package, and I don't have a clear > and easy way to tell whether the function will make the > error call or not, it would be easy just to make a wrapper > like that. It might be easy, but if you didn't have a lot of insight into the function's behavior, then it would be difficult to tell whether it's really going to call error or whether it's going to go off into an infinite loop. (Consider the (slow) definition x ^ n | n == 0= 1 | n < 0= error "Negative exponents require ^^" | otherwise = x * x ^ (n - 1) Now consider what happens if the library function forgets the second case. Your wrapper isn't safe anymore!) I can see only two cases where a library function could call error sometimes, and you wouldn't have a good feel for when: a) The function is calling error on exceptions. You should bug the library author to put the function into an exception monad instead. Devil-may-care users can use either (error . show) id to turn exceptions into errors. b) The function has explicit pre-conditions, which you don't understand. You shouldn't pass arguments to a function that violate its pre-conditions (ever!); if you don't understand those preconditions well enough to test them in Haskell code, you might not understand them well enough to make sure your code is calling the function correctly. So you might want to study the preconditions a little more. > It's also a possible situation that I don't know > how to test the input to a foreign function call. FFI calls cannot throw Haskell exceptions. jcc ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Henning Thielemann writes: > On Thu, 26 Mar 2009, Xiao-Yong Jin wrote: > >> So I have another question. Is the following function safe >> and legitimate? >> >>> safeDiv :: (Exception e, Integral a) => >>>a -> a -> Either e a >>> safeDiv x y = unsafePerformIO . try . evaluate $ div x y >> >> I believe it should be okay to use this 'safeDiv'. What do > > I think that question is wrong way around. The real question is, why > do you want to solve your problem using unsafePerformIO? I just want to know, from a theoretical point of view, whether this 'safeDiv' in above definition is the same as > safeDiv' :: (Exception e, Integral a) => > a -> a -> Either e a > safeDiv' _ 0 = Left e > safeDiv' x y = Right $ div x y For the question why do I want to do that, I am not sure. I guess if the function which has an error call inside is provided by other library package, and I don't have a clear and easy way to tell whether the function will make the error call or not, it would be easy just to make a wrapper like that. It's also a possible situation that I don't know how to test the input to a foreign function call. -- c/*__o/* <\ * (__ */\ < ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
> Message: 15 > From: Xiao-Yong Jin > John Lato writes: > > So I have another question. Is the following function safe > and legitimate? > >> safeDiv :: (Exception e, Integral a) => >> a -> a -> Either e a >> safeDiv x y = unsafePerformIO . try . evaluate $ div x y > > I believe it should be okay to use this 'safeDiv'. What do > you think? Your question is unclear. Are you asking if this function can be safely used with current Haskell standards and implementations or are you asking should the Haskell specification make a guarantee that this function will do what you want? I would answer "no" either way, but for different reasons. Common to both is: Don't use unsafe* functions unless you can prove that your usage is safe. That is proof as in mathematical usage; it must be rigorous and complete. John Lato ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Thu, 26 Mar 2009, Xiao-Yong Jin wrote: So I have another question. Is the following function safe and legitimate? safeDiv :: (Exception e, Integral a) => a -> a -> Either e a safeDiv x y = unsafePerformIO . try . evaluate $ div x y I believe it should be okay to use this 'safeDiv'. What do I think that question is wrong way around. The real question is, why do you want to solve your problem using unsafePerformIO? ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
John Lato writes: > Even in Haskell this separation isn't absolute. Programmer errors, > such as dividing by 0, can and do lead to exceptional conditions. The > proper way to handle dividing by 0 is to not do it in the first place, > but if it happens because of a programming error, you've got an > exception. Unfortunately this encourages programmers to think that > handling the exception is the proper way to deal with this condition, > but it isn't. So I have another question. Is the following function safe and legitimate? > safeDiv :: (Exception e, Integral a) => >a -> a -> Either e a > safeDiv x y = unsafePerformIO . try . evaluate $ div x y I believe it should be okay to use this 'safeDiv'. What do you think? -- c/*__o/* <\ * (__ */\ < ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Wed, Mar 25, 2009 at 7:02 PM, Gregory Petrosyan wrote: > First of all, thanks everybody for the discussion -- very interesting to read! > > Apologies if this is off-topic a bit. > > While reading, I have a feeling that proposed solutions are somewhat similar > to checked exceptions. And IMO they turned out to be more harmful than useful. Do you mean checked exceptions, or the proposed solutions? IMO the problem with checked exceptions is that they conflate two ideas by trying to use the existing exception framework to do something that can't otherwise be done by the language. This leads to something that doesn't work well for either use. Languages with checked exceptions usually use them for two purposes: 1. Exceptional conditions - disk full, CPU on fire, etc. 2. Error handling - invalid arguments to a function, attempt to invert non-invertible matrix, etc. In the first case, checked exceptions are a pain because there are so many different possible exceptional conditions to enumerate. Or alternatively you have an IOException that can be one of hundreds of actual exceptional conditions. This is exactly what regular exceptions are for, and in general there's little that can be done except terminate the program, except for certain special cases explicitly handled by the programmer. For the second case, checked exceptions actually work okay IMO. The syntax is a bit clunky, but they serve the purpose. That is, they specifically indicate what functions may fail and the failure mode. They also allow calling code to either handle it there or pass the error up to a higher level as appropriate. The syntax is usually ugly, though. Combining these two functions into one tool gives suboptimal results. If you're using checked exceptions for error handling, then you also end up using them for exception handling. That means you have IOException thrown by just about everything (and in theory any function in an imperative language could have an IOException due to hardware fault), leading to complex exception handlers with a half dozen case statements. There are a few reasons why Haskell's approach is (or at least can be) different. Compare the error handling models. Checked exceptions are bolted on to an existing tool attempting to make it serve a different purpose. It seems like a good idea, but ends up being painful because the two uses are at cross purposes. Instead of using a specific implementation and trying to alter it for another use, Haskell uses a very general facility, the type system, to solve one problem, and keeps the exception handling facility separate. Haskell's syntax for error handling is also much nicer. Nobody likes using methods that could possibly throw a dozen different exceptions, some of which are IOException and some are not. Actually, Haskell IO makes a big difference to this. Conceptually, you could think of Haskell functions as purely mathematical constructs. In particular, they can't interact with the physical world. This is true even for the IO monad. Exceptional conditions only arise when the Haskell runtime actually tries to *evaluate* an IO function. As a consequence exceptional conditions only arise as part of IO actions, and can only be handled by IO actions (because they're interacting with the run-time environment). This distinction makes it possible to enforce a separation between exceptions and error conditions, which is generally not possible in imperative languages where a function could fail either for a computational reason (divide by 0) or a hardware/runtime fault. Even in Haskell this separation isn't absolute. Programmer errors, such as dividing by 0, can and do lead to exceptional conditions. The proper way to handle dividing by 0 is to not do it in the first place, but if it happens because of a programming error, you've got an exception. Unfortunately this encourages programmers to think that handling the exception is the proper way to deal with this condition, but it isn't. I've only recently come around to the camp of treating exception handling and errors separately, so some of these thoughts may be a bit loose for the moment. In particular my thoughts from the above paragraph have only recently become clear. Henning T., FYI your constant advocacy has gotten at least one person around to this view. Cheers, John Lato ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
John Lato writes: >> Yes, I know, it's not really complicate to rewrite the above >> code. But, what do I really gain from this rewrite? > > Apologies if this discussion has moved on, but I wanted to comment on this. > Thanks for elaborating it more. > > You gain correctness. Any functions that need to be >rewritten in this > case should be rewritten anyway, because they're already wrong. Your > function ff can fail for certain inputs. This statement: >>> | It is impractical to use method (a), >>> | because not every function that uses 'invMat' knows how to >>> | deal with 'invMat' not giving an answer. So we need to use >>> | method (b), to use monad to parse our matrix around. > > is conceptually wrong. What does it mean to multiply the inverse of a > non-invertible matrix by a scalar? Obviously this is nonsensical. If > a computation can fail (as this can), the type of the function should > reflect it. The above functions > >>> f1 = scalarMult 2 . invMat >>> f2 l r = l `multMat` invMat r > > should be > > f1 :: Matrix -> Maybe Matrix > f1 = fmap (scalarMult 2) . invMat > > f2 :: Matrix -> Matrix -> Maybe Matrix > f2 l r = fmap (multMat l) $ invMat r > > Of course these could be written with Control.Applicative as well: > > f1 m = scalarMult 2 <$> invMat m > f2 l r = multMat l <$> invMat r > > >>> ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix >>> ff x y = let ff' = f1 x + f2 y > ... > in scalarMult (1/2) ff' > > (I think you may be missing an argument to f2 here.) > > This computation can fail as well, if the constituent parts fail. The > separate parts can be combined with applicative style: > > ff :: Matrix -> Matrix -> Maybe Matrix > ff x y = scalarMult (1/2) <$> ( (+) <$> f1 x <*> f2 y) > > Compare this to the same code using monadic Maybe: > > ff :: Matrix -> Matrix -> Maybe Matrix > ff x y = do > x' <- f1 x > y' <- f2 y > scalarMult (1/2) $ x' + y' > > You gain clarity and brevity. Both examples are shorter and easier to > understand because you aren't messing with all the plumbing of error > handling using exceptions, although I find the Applicative version > especially clear. If you would like to keep track of why a > computation failed, then use Either instead of Maybe with the Left > carrying a reason for failure (e.g. NonInvertibleMatrix) > > Finally, you gain safety. When you use a function f1 :: Matrix -> > Matrix, you can be assured that you will get an actual, meaningful > answer. If you use a function f2 :: Matrix -> Maybe Matrix, you know > that you may not get a meaningful answer, and it is simple to handle > at the appropriate level of your code. I (and many other Haskell > users) find this to be conceptually cleaner than throwing dynamic > exceptions or using undefined. > > Incidentally, this is one reason why many experienced Haskellers like > the applicative style. It allows you to express your computations > without obtrusive error handling mixed in. It's also more general > than monads, so can be applied in more instances. > > div (and other non-total functions in the Prelude like head), are also > frequently considered ugly hacks. Just because we're stuck with > something from H98 doesn't mean that it's necessarily good or elegant > (the fail monad method and Functor not being a superclass of Monad > come to mind). In some ways FP has moved on since Haskell was > formalized. > > There is an alternative approach that I believe was suggested by > somebody else on the list: > > newtype InvMatrix = Invert {unWrap :: Matrix} > > then you can do > invertMatrix :: Matrix -> Maybe InvMatrix > invertMatrix = fmap Invert . invMat > > If you put these in a separate module and export InvMatrix, unwrap, > and invertMatrix, but not Invert, then the only way to create an > InvMatrix is with invertMatrix, so any data of type InvMatrix is > guaranteed to be invertible (and inverted from what you used to create > it). > > Then your ff function becomes: > > ff :: InvMatrix -> InvMatrix -> Matrix > > the final value of the function could be InvMatrix if you can prove > that it's invertible after your operations (although to be efficient, > this would require exporting the Invert constructor and a proof from > the programmer). This keeps ff pure; you don't even have to deal with > Maybe (although there are other ramifications to doing this that > should be considered). > > John > > > -- c/*__o/* <\ * (__ */\ < ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
> Jake McArthur writes: > >> Xiao-Yong Jin wrote: >> | The problem is that there will be many functions using such >> | a function to invert a matrix, making this inversion >> | function return Either/Maybe or packing it in a monad is >> | just a big headache. >> >> I disagree. If you try to take the inverse of a noninvertable matrix, >> this is an *error* in your code. Catching an error you created in pure >> code and patching it with chewing gum it is just a hack. A monadic >> approach (I'm putting Either/Maybe under the same umbrella for brevity) >> is the only solution that makes any sense to me, and I don't think it's >> ugly as you are making it out to be. >> > > Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? > Why does it throws this /ugly/ /error/ when it is applied to > 0? Why is it not using some beautiful > 'ArithExceptinoMonad'? Is 'Control.Exception' just pure > /ugly/ and doesn't make any sense? > >> >> | It is impractical to use method (a), >> | because not every function that uses 'invMat' knows how to >> | deal with 'invMat' not giving an answer. So we need to use >> | method (b), to use monad to parse our matrix around. >> | >> |> > invMat :: Matrix -> NumericCancerMonad Matrix >> | >> | It hides the exceptional nature of numerical computations >> | very well, but it is cancer in the code. Whenever any >> | function wants to use invMat, it is mutated. This is just >> | madness. You don't want to make all the code to be monadic >> | just because of singularities in numeric calculation. >> >> For functions that don't know or don't care about failure, just use fmap >> or one of its synonyms. >> >> ~ scalarMult 2 <$> invMat x >> >> See? The scalarMult function is pure, as it should be. There is no >> madness here. > > Of course, 'scalarMult' is invulnerable and free of monad. > But take a look at the following functions, > >> f1 = scalarMult 2 . invMat >> f2 l r = l `multMat` invMat r >> ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix >> ff x y = do let ff' = f1 x + f2 y >> put . (addMat ff') . f1 << get >> tell $ f2 ff' >> when (matrixWeDontLike (f1 ff') $ >> throwError MatrixWeDontLike >> return $ scalarMult (1/2) ff' > > Yes, I know, it's not really complicate to rewrite the above > code. But, what do I really gain from this rewrite? Apologies if this discussion has moved on, but I wanted to comment on this. You gain correctness. Any functions that need to be rewritten in this case should be rewritten anyway, because they're already wrong. Your function ff can fail for certain inputs. This statement: >> | It is impractical to use method (a), >> | because not every function that uses 'invMat' knows how to >> | deal with 'invMat' not giving an answer. So we need to use >> | method (b), to use monad to parse our matrix around. is conceptually wrong. What does it mean to multiply the inverse of a non-invertible matrix by a scalar? Obviously this is nonsensical. If a computation can fail (as this can), the type of the function should reflect it. The above functions >> f1 = scalarMult 2 . invMat >> f2 l r = l `multMat` invMat r should be f1 :: Matrix -> Maybe Matrix f1 = fmap (scalarMult 2) . invMat f2 :: Matrix -> Matrix -> Maybe Matrix f2 l r = fmap (multMat l) $ invMat r Of course these could be written with Control.Applicative as well: f1 m = scalarMult 2 <$> invMat m f2 l r = multMat l <$> invMat r >> ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix >> ff x y = let ff' = f1 x + f2 y ... in scalarMult (1/2) ff' (I think you may be missing an argument to f2 here.) This computation can fail as well, if the constituent parts fail. The separate parts can be combined with applicative style: ff :: Matrix -> Matrix -> Maybe Matrix ff x y = scalarMult (1/2) <$> ( (+) <$> f1 x <*> f2 y) Compare this to the same code using monadic Maybe: ff :: Matrix -> Matrix -> Maybe Matrix ff x y = do x' <- f1 x y' <- f2 y scalarMult (1/2) $ x' + y' You gain clarity and brevity. Both examples are shorter and easier to understand because you aren't messing with all the plumbing of error handling using exceptions, although I find the Applicative version especially clear. If you would like to keep track of why a computation failed, then use Either instead of Maybe with the Left carrying a reason for failure (e.g. NonInvertibleMatrix) Finally, you gain safety. When you use a function f1 :: Matrix -> Matrix, you can be assured that you will get an actual, meaningful answer. If you use a function f2 :: Matrix -> Maybe Matrix, you know that you may not get a meaningful answer, and it is simple to handle at the appropriate level of your code. I (and many other Haskell users) find this to be conceptually cleaner than throwing dynamic exceptions or using undefined. Incidentally, this is one reason why many experienced Haskellers like the applicative style. It allows yo
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Tue, 24 Mar 2009, Xiao-Yong Jin wrote: Thanks for all the replies. Now I understand more about Exceptions and Errors. I guess all I need is to compose a larger monad, after all. I need to learn how to make two different stacks of monad transformers cooperate seamlessly, though. Until now it seems you only need Applicative functor. They can be combined in a more general way: http://hackage.haskell.org/packages/archive/TypeCompose/0.6.4/doc/html/Control-Compose.html See the instances: (Applicative g, Applicative f) => Applicative (g :. f) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
Jake McArthur writes: > Xiao-Yong Jin wrote: > | Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? > | Why does it throws this /ugly/ /error/ when it is applied to > | 0? Why is it not using some beautiful > | 'ArithExceptinoMonad'? Is 'Control.Exception' just pure > | /ugly/ and doesn't make any sense? > > 'div' throws an error because dividing by zero is *programmer error*. > *You* are supposed to make sure that you aren't dividing by zero. > > I differ from this decision in your case because, as you said, it is > easier to check for the error condition in the function itself than to > check it externally. This is fine, but because it's so hard to check > externally, you have to tell the outside world whether there was an > error or not. A functor/applicative/monad is the pure way to do this. An > error is not. > > | Of course, 'scalarMult' is invulnerable and free of monad. > | But take a look at the following functions, > | > |> f1 = scalarMult 2 . invMat > |> f2 l r = l `multMat` invMat r > |> ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix > |> ff x y = do let ff' = f1 x + f2 y > |> put . (addMat ff') . f1 << get > |> tell $ f2 ff' > |> when (matrixWeDontLike (f1 ff') $ > |> throwError MatrixWeDontLike > |> return $ scalarMult (1/2) ff' > | > | Yes, I know, it's not really complicate to rewrite the above > | code. But, what do I really gain from this rewrite? > > Code that is fully documented by its type, no harder to compose, more > pure, and does what the programmer expects it to do. Thanks for all the replies. Now I understand more about Exceptions and Errors. I guess all I need is to compose a larger monad, after all. I need to learn how to make two different stacks of monad transformers cooperate seamlessly, though. Thanks, Xiao-Yong -- c/*__o/* <\ * (__ */\ < ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
-BEGIN PGP SIGNED MESSAGE- Hash: SHA1 Xiao-Yong Jin wrote: | Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? | Why does it throws this /ugly/ /error/ when it is applied to | 0? Why is it not using some beautiful | 'ArithExceptinoMonad'? Is 'Control.Exception' just pure | /ugly/ and doesn't make any sense? 'div' throws an error because dividing by zero is *programmer error*. *You* are supposed to make sure that you aren't dividing by zero. I differ from this decision in your case because, as you said, it is easier to check for the error condition in the function itself than to check it externally. This is fine, but because it's so hard to check externally, you have to tell the outside world whether there was an error or not. A functor/applicative/monad is the pure way to do this. An error is not. | Of course, 'scalarMult' is invulnerable and free of monad. | But take a look at the following functions, | |> f1 = scalarMult 2 . invMat |> f2 l r = l `multMat` invMat r |> ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix |> ff x y = do let ff' = f1 x + f2 y |> put . (addMat ff') . f1 << get |> tell $ f2 ff' |> when (matrixWeDontLike (f1 ff') $ |> throwError MatrixWeDontLike |> return $ scalarMult (1/2) ff' | | Yes, I know, it's not really complicate to rewrite the above | code. But, what do I really gain from this rewrite? Code that is fully documented by its type, no harder to compose, more pure, and does what the programmer expects it to do. - - Jake -BEGIN PGP SIGNATURE- Version: GnuPG v1.4.9 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iEYEARECAAYFAknJXpcACgkQye5hVyvIUKnTOwCgzqRC4i7eLgbOQW1r+u2NPhAQ 7NkAnRsOFE8uMWrB/TRxTfdP/+x35EZ8 =kCtc -END PGP SIGNATURE- ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Tue, 24 Mar 2009, Xiao-Yong Jin wrote: Jake McArthur writes: Xiao-Yong Jin wrote: | The problem is that there will be many functions using such | a function to invert a matrix, making this inversion | function return Either/Maybe or packing it in a monad is | just a big headache. I disagree. If you try to take the inverse of a noninvertable matrix, this is an *error* in your code. Catching an error you created in pure code and patching it with chewing gum it is just a hack. A monadic approach (I'm putting Either/Maybe under the same umbrella for brevity) is the only solution that makes any sense to me, and I don't think it's ugly as you are making it out to be. Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? Why does it throws this /ugly/ /error/ when it is applied to 0? I think "throw" should be reserved to exceptions (although it is still strange English). Actually 'div x 0' is 'undefined', just like in mathematics. This is justified by the fact, that you can easily check whether the denominator is zero or not and it is expected that either you check the denominator before calling 'div' or that you proof that in your application the denominator is non-zero anyway and thus save repeated checks for zero at run-time. The deficiency is not in 'div' but in the type system, which does not allow to declare an Int to be non-zero. In contrast to that, it is not easily checked, whether a matrix is regular. Thus you may prefer a Maybe result. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
Daniel Yokomizo writes: > On Tue, Mar 24, 2009 at 1:27 PM, Xiao-Yong Jin wrote: >> Henning Thielemann writes: >> >>> Try to never use exception handling for catching programming errors! >>> Division by zero is undefined, thus a programming error when it >>> occurs. >>> http://www.haskell.org/haskellwiki/Error >>> http://www.haskell.org/haskellwiki/Exception >>> I'm afraid, a Maybe or Either or Exceptional (see explicit-exception >>> package) return value is the only way to handle exceptional return >>> values properly. Maybe in the larger context of your problem zero >>> denominators can be avoided? Then go this way. >> >> Using div is just an example I'm testing with what I read in >> the book Real World Haskell. The real thing I'm trying to >> do is inverting a matrix. Say, I want to write >> >>> invMat :: Matrix -> Matrix >> >> You won't be able to invert all the matrix, mathematically. >> And computationally, even a larger set of matrix might fail >> to be inverted because of the finite precision. It is >> relatively easier and more efficient to spot such a problem >> within this 'invMat' function. Because testing the >> singularity of a matrix is equally hard as invert it. So >> all I can do when 'invMat' spot a singular matrix are >> >> a) Return Either/Maybe to signal an error. >> b) Wrap it in a monad. >> c) Define a dynamic exception and throw it. > > In general if a function is partial we can either make it total by > extending its range or restricting its domain. Also we can signal it > using runtime or compile-time mechanisms. Options a & b are equivalent > (i.e. extend the range, compile-time notification) and option c is > also another way of extending the range, but using runtime > notification. > > If we try the other approach, we need to express the totality of > invMat by restricting its domain, so we can add, for example, a > phantom type to Matrix to signal it is invertible. As you need to > construct the Matrix before trying to invert it you can always make > the constructors smart enough to bundle the Matrix with such > properties. Of course there's need to do some runtime verifications > earlier, but the clients of invMat are required to do the verification > earlier or pass it to their clients (up to the level that can handle > with this issue): > > data Invertible > tryInvertible :: Matrix a -> Maybe (Matrix Invertible) > invMat :: Matrix Invertible -> Matrix Invertible > > > You could use different forms of evidence (e.g. phantom types, type > classes) but the idea is the same. This is theoretically sound, we can make a type 'Integer Invertible' and make a 'safeDiv' to get rid of one of the ArithException. But as I said above, "testing the singularity of a matrix is equally hard as inverting it", doing it with matrix is more impractical than make 'div' safe. I don't mind my haskell code runs 6 times slower than c++ code. But I'm not ready to make it 10 times slower just because I want to do something twice and it might as well be useless most of the time. -- c/*__o/* <\ * (__ */\ < ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Tue, Mar 24, 2009 at 3:28 PM, Luke Palmer wrote: > On Tue, Mar 24, 2009 at 3:14 PM, Xiao-Yong Jin wrote: > >> Jake McArthur writes: >> >> > Xiao-Yong Jin wrote: >> > | The problem is that there will be many functions using such >> > | a function to invert a matrix, making this inversion >> > | function return Either/Maybe or packing it in a monad is >> > | just a big headache. >> > >> > I disagree. If you try to take the inverse of a noninvertable matrix, >> > this is an *error* in your code. Catching an error you created in pure >> > code and patching it with chewing gum it is just a hack. A monadic >> > approach (I'm putting Either/Maybe under the same umbrella for brevity) >> > is the only solution that makes any sense to me, and I don't think it's >> > ugly as you are making it out to be. >> > >> >> Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? >> Why does it throws this /ugly/ /error/ when it is applied to >> 0? Why is it not using some beautiful >> 'ArithExceptinoMonad'? Is 'Control.Exception' just pure >> /ugly/ and doesn't make any sense? > > > It's a proof obligation, like using unsafePerformIO. It is "okay" to use > unsafePerformIO when it exhibits purely functional semantics, but it's > possible to use it incorrectly, and there is no ImpureSemanticsException. > If you are being rigorous, you simply have to prove that the denominator > will not be zero, rather than relying on it to be caught at runtime. You > can move the check to runtime easily: > > safeDiv x 0 = Nothing > safeDiv x y = Just (x `div` y) > > Going the other way, from a runtime check to an obligation, is impossible. > (well, except for div x y = fromJust (safeDiv x y).. but the runtime check is still there in terms of operation) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: Exception handling in numeric computations
On Tue, Mar 24, 2009 at 3:14 PM, Xiao-Yong Jin wrote: > Jake McArthur writes: > > > Xiao-Yong Jin wrote: > > | The problem is that there will be many functions using such > > | a function to invert a matrix, making this inversion > > | function return Either/Maybe or packing it in a monad is > > | just a big headache. > > > > I disagree. If you try to take the inverse of a noninvertable matrix, > > this is an *error* in your code. Catching an error you created in pure > > code and patching it with chewing gum it is just a hack. A monadic > > approach (I'm putting Either/Maybe under the same umbrella for brevity) > > is the only solution that makes any sense to me, and I don't think it's > > ugly as you are making it out to be. > > > > Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? > Why does it throws this /ugly/ /error/ when it is applied to > 0? Why is it not using some beautiful > 'ArithExceptinoMonad'? Is 'Control.Exception' just pure > /ugly/ and doesn't make any sense? It's a proof obligation, like using unsafePerformIO. It is "okay" to use unsafePerformIO when it exhibits purely functional semantics, but it's possible to use it incorrectly, and there is no ImpureSemanticsException. If you are being rigorous, you simply have to prove that the denominator will not be zero, rather than relying on it to be caught at runtime. You can move the check to runtime easily: safeDiv x 0 = Nothing safeDiv x y = Just (x `div` y) Going the other way, from a runtime check to an obligation, is impossible. > > > > > | It is impractical to use method (a), > > | because not every function that uses 'invMat' knows how to > > | deal with 'invMat' not giving an answer. So we need to use > > | method (b), to use monad to parse our matrix around. > > | > > |> > invMat :: Matrix -> NumericCancerMonad Matrix > > | > > | It hides the exceptional nature of numerical computations > > | very well, but it is cancer in the code. Whenever any > > | function wants to use invMat, it is mutated. This is just > > | madness. You don't want to make all the code to be monadic > > | just because of singularities in numeric calculation. > > > > For functions that don't know or don't care about failure, just use fmap > > or one of its synonyms. > > > > ~scalarMult 2 <$> invMat x > > > > See? The scalarMult function is pure, as it should be. There is no > > madness here. > > Of course, 'scalarMult' is invulnerable and free of monad. > But take a look at the following functions, > > > f1 = scalarMult 2 . invMat > > f2 l r = l `multMat` invMat r > > ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix > > ff x y = do let ff' = f1 x + f2 y > > put . (addMat ff') . f1 << get > > tell $ f2 ff' > > when (matrixWeDontLike (f1 ff') $ > > throwError MatrixWeDontLike > > return $ scalarMult (1/2) ff' > > Yes, I know, it's not really complicate to rewrite the above > code. But, what do I really gain from this rewrite? > -- >c/*__o/* ><\ * (__ >*/\ < > ___ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://www.haskell.org/mailman/listinfo/haskell-cafe > ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: Exception handling in numeric computations
Jake McArthur writes: > Xiao-Yong Jin wrote: > | The problem is that there will be many functions using such > | a function to invert a matrix, making this inversion > | function return Either/Maybe or packing it in a monad is > | just a big headache. > > I disagree. If you try to take the inverse of a noninvertable matrix, > this is an *error* in your code. Catching an error you created in pure > code and patching it with chewing gum it is just a hack. A monadic > approach (I'm putting Either/Maybe under the same umbrella for brevity) > is the only solution that makes any sense to me, and I don't think it's > ugly as you are making it out to be. > Then, why is 'div' not of type 'a -> a -> ArithExceptionMonad a' ? Why does it throws this /ugly/ /error/ when it is applied to 0? Why is it not using some beautiful 'ArithExceptinoMonad'? Is 'Control.Exception' just pure /ugly/ and doesn't make any sense? > > | It is impractical to use method (a), > | because not every function that uses 'invMat' knows how to > | deal with 'invMat' not giving an answer. So we need to use > | method (b), to use monad to parse our matrix around. > | > |> > invMat :: Matrix -> NumericCancerMonad Matrix > | > | It hides the exceptional nature of numerical computations > | very well, but it is cancer in the code. Whenever any > | function wants to use invMat, it is mutated. This is just > | madness. You don't want to make all the code to be monadic > | just because of singularities in numeric calculation. > > For functions that don't know or don't care about failure, just use fmap > or one of its synonyms. > > ~scalarMult 2 <$> invMat x > > See? The scalarMult function is pure, as it should be. There is no > madness here. Of course, 'scalarMult' is invulnerable and free of monad. But take a look at the following functions, > f1 = scalarMult 2 . invMat > f2 l r = l `multMat` invMat r > ff :: Matrix -> Matrix -> YetAnotherBiggerMonad Matrix > ff x y = do let ff' = f1 x + f2 y > put . (addMat ff') . f1 << get > tell $ f2 ff' > when (matrixWeDontLike (f1 ff') $ > throwError MatrixWeDontLike > return $ scalarMult (1/2) ff' Yes, I know, it's not really complicate to rewrite the above code. But, what do I really gain from this rewrite? -- c/*__o/* <\ * (__ */\ < ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe