RE: Exceptions
On 02 September 2006 06:29, Ashley Yakeley wrote: I'd also like to query O'Haskell here. Simon writes in the paper: O'Haskell extends Haskell with object-oriented subtyping. As such, it would be entirely possible to implement extensible exceptions using inheritance in O'Haskell. I believe O'Haskell (like OOHaskell) doesn't provide the required dynamic downcasting operation either. AFAICT the extensions are essentially syntactic sugar: the subtyping is strictly static. Good point; thanks for spotting that. Simon ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
Hi Ashley. Thanks for your interest in open data types. As one of the authors of the open data types paper, I'd like to comment on the current discussion. You comment Simon's upcoming HW paper on extensible exceptions: You write: Compared to our approach, theirs requires new extensions to the language (although not deep), Typeable is an extension to Haskell, and a rather ugly one at that. The open datatypes extension is both cleaner and more general. I think the stress here is on *new* extensions. I agree that an open type of type representations might be a more beautiful solution to the problem that Typeable solves. Nevertheless, the fact is that Simon's solution can be used in current GHC without further implementation work. and has difficulties with separate compilation. They claim to solve this I think, though I haven't examined it really carefully. You may know better, of course. I've discussed this with Simon PJ. Apart from minor technical problems, everything seems doable in GHC, but it is quite some work and it's not clear that I will have the time to study GHC closely enough to do it in the near future. I hope I can say more after the Hackathon ... Arguably the open data types approach is more direct and more accessible, Yes, as is often the case with extensions designed to solve a particular problem. That's not fair. Open datatypes have other applications. A general file interpreter for instance, that given a MIME type string and a list of bytes yields an object. Or a collection of variable resources of various types that could be passed to a program. Or a hierarchy of UI widgets. Or anything that Typeable and Dynamic are currently used for, but more cleanly. Hs-plugins, for instance. It's the missing piece. True, open data types have never been invented as a solution to the problem of extensible exceptions. It is an application that we found afterwards. Still, the argument for adding open data types to the language is weakened by the fact that they are subsumed by type classes: in fact the authors give an encoding of open data types into type classes, Well not really. The encoding involves lifting everything from values to types, which means a function still can't return a value of an open type determined at run-time. Even if both approaches would be equally expressive, the type class encoding still has a lot of syntactic overhead. Moving from a closed to an open data type encoded by type classes requires changing your whole program, whereas with open data types, it is a local change. Apart from this discussion however, open data types are clearly not Haskell' material, because the proposal is new and currently unimplemented. The extensions required for Simon's approach to exceptions have a good chance of being included in Haskell'. Cheers, Andres ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
In article [EMAIL PROTECTED], Andres Loeh [EMAIL PROTECTED] wrote: I think the stress here is on *new* extensions. I agree that an open type of type representations might be a more beautiful solution to the problem that Typeable solves. Nevertheless, the fact is that Simon's solution can be used in current GHC without further implementation work. OK, fair enough. Open datatypes and functions will not, as you point out, be in Haskell'. I'd also like to query O'Haskell here. Simon writes in the paper: O'Haskell extends Haskell with object-oriented subtyping. As such, it would be entirely possible to implement extensible exceptions using inheritance in O'Haskell. I believe O'Haskell (like OOHaskell) doesn't provide the required dynamic downcasting operation either. AFAICT the extensions are essentially syntactic sugar: the subtyping is strictly static. -- Ashley Yakeley, Seattle WA ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
RE: Exceptions
On 30 August 2006 20:20, Ashley Yakeley wrote: John Goerzen wrote: One thing that bugs me about Haskell is that exceptions are not extensible. I don't know how to craft a good solution, but perhaps if I explain the problem well, someone would come up with one. Open datatypes would be the best solution, I think. http://www.informatik.uni-bonn.de/~loeh/OpenDatatypes.pdf I don't think we need more extensions to do a reasonable job of extensible exceptions: http://www.haskell.org/~simonmar/papers/ext-exceptions.pdf Cheers, Simon ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
In article [EMAIL PROTECTED] ft.com, Simon Marlow [EMAIL PROTECTED] wrote: I don't think we need more extensions to do a reasonable job of extensible exceptions: http://www.haskell.org/~simonmar/papers/ext-exceptions.pdf You write: Compared to our approach, theirs requires new extensions to the language (although not deep), Typeable is an extension to Haskell, and a rather ugly one at that. The open datatypes extension is both cleaner and more general. and has difficulties with separate compilation. They claim to solve this I think, though I haven't examined it really carefully. You may know better, of course. Arguably the open data types approach is more direct and more accessible, Yes, as is often the case with extensions designed to solve a particular problem. That's not fair. Open datatypes have other applications. A general file interpreter for instance, that given a MIME type string and a list of bytes yields an object. Or a collection of variable resources of various types that could be passed to a program. Or a hierarchy of UI widgets. Or anything that Typeable and Dynamic are currently used for, but more cleanly. Hs-plugins, for instance. It's the missing piece. Still, the argument for adding open data types to the language is weakened by the fact that they are subsumed by type classes: in fact the authors give an encoding of open data types into type classes, Well not really. The encoding involves lifting everything from values to types, which means a function still can't return a value of an open type determined at run-time. -- Ashley Yakeley Seattle WA ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
John Goerzen wrote: One thing that bugs me about Haskell is that exceptions are not extensible. I don't know how to craft a good solution, but perhaps if I explain the problem well, someone would come up with one. Open datatypes would be the best solution, I think. http://www.informatik.uni-bonn.de/~loeh/OpenDatatypes.pdf -- Ashley Yakeley Seattle, WA ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
RE: Exceptions
Attached is another variant of the extensible exceptions idea, it improves on the previous designs in a couple of ways: there's only one catch throw, regardless of what type you're throwing or catching. There is an extensible hierarchy of exceptions, and you can catch and re-throw subclasses of exceptions. So this design contains a dynamically-typed extensible hierarchy, but it's fairly lightweight. Adding a new leaf exception type requires 3 lines + 1 line for each superclass (just 1 line for a top level leaf, as before). Adding a new node requires about 10 lines + 1 line for each superclass, msotly boilerplate. Perhaps the type class hackers can do better than this! Cheers, Simon Exception-2.hs Description: Exception-2.hs ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
On Tue, Apr 11, 2006 at 01:24:07PM +0100, Simon Marlow wrote: Attached is another variant of the extensible exceptions idea, it improves on the previous designs in a couple of ways: there's only one catch throw, regardless of what type you're throwing or catching. There is an extensible hierarchy of exceptions, and you can catch and re-throw subclasses of exceptions. I made the catch and throw separate so the decision as to whether to include imprecice exceptions and extensible extensions can be made independently. that and throw x /= ioError x ioError x return () - IO _|_ (only _|_ when IO action executed) throw x return () - _|_ ioError x `seq` () - () throw x `seq` () - _|_ John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
On Tue, Apr 11, 2006 at 05:35:12AM -0700, John Meacham wrote: throw x return () - _|_ hmm.. actually is this true? hmm.. seq and IO always mixed oddly. John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
RE: Exceptions
On 11 April 2006 13:35, John Meacham wrote: On Tue, Apr 11, 2006 at 01:24:07PM +0100, Simon Marlow wrote: Attached is another variant of the extensible exceptions idea, it improves on the previous designs in a couple of ways: there's only one catch throw, regardless of what type you're throwing or catching. There is an extensible hierarchy of exceptions, and you can catch and re-throw subclasses of exceptions. I made the catch and throw separate so the decision as to whether to include imprecice exceptions and extensible extensions can be made independently. that and throw x /= ioError x ioError x return () - IO _|_ (only _|_ when IO action executed) throw x return () - _|_ ioError x `seq` () - () throw x `seq` () - _|_ yes, when I say one throw I was referring to the argument type, not the return type. We should still have ioError - although it would probably be better named throwIO: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Excep tion.html#v%3AthrowIO (the docs for throwIO also mention the strictness property you described above) Cheers, Simon ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
On Tue, Apr 11, 2006 at 01:43:18PM +0100, Simon Marlow wrote: yes, when I say one throw I was referring to the argument type, not the return type. We should still have ioError - although it would probably be better named throwIO: Ah, I see what you mean now. would it be possible to use Typeable1 to just catch 'ArithException a' for any Typeable a? It seems like it should be, but I have not used Typeable1 much. John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
RE: Exceptions
On 11 April 2006 13:54, John Meacham wrote: On Tue, Apr 11, 2006 at 01:43:18PM +0100, Simon Marlow wrote: yes, when I say one throw I was referring to the argument type, not the return type. We should still have ioError - although it would probably be better named throwIO: Ah, I see what you mean now. would it be possible to use Typeable1 to just catch 'ArithException a' for any Typeable a? It seems like it should be, but I have not used Typeable1 much. I tried it briefly and couldn't get it to work, but I'm no expert on the SYB stuff. You might need an Exception1 class to go with Typeable1, and that would be fairly ugly. Cheers, Simon ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
Re: Exceptions
John Meacham writes: On Fri, Apr 07, 2006 at 10:00:21AM -0500, John Goerzen wrote: But here's my concern. Let's say that I wanted to, for some reason, create a MultiplyByZero exception. It should be broadly considered an ArithException, and any code that catches an ArithException should be able to catch my MultiplyByZero exception. But the ArithException type is limited to storing errors that are defined by Control.Exception.ArithException. My MultiplyByZero is not defined there, so I am out of luck. The best I could do is define a new MultiplyByZero, and catch it in my own code. But any code that others have written to catch ArithExceptions would be blind to MultiplyByZero. newtype ArithException a = ArithException a data DivideByZero throw (ArithException DivideByZero) your code: data MultiplyByZero throw (ArithException MultiplyByZero) How would you use this to write a handler that captures any ArithException? -- David Menendez [EMAIL PROTECTED] | In this house, we obey the laws http://www.eyrie.org/~zednenem |of thermodynamics! ___ Haskell-prime mailing list Haskell-prime@haskell.org http://haskell.org/mailman/listinfo/haskell-prime
[Haskell] Re: Exceptions
On 2004-11-23, Johannes Waldmann [EMAIL PROTECTED] wrote: in the following example, the handler won't catch the exception because of lazy evaluation. therefore, it's a different story than with exceptions in ML, Python, whatever strict language. main = do xs - return [ 1, 2, error throw ] `catch` \ any - do putStrLn caught return [ 4, 5, 6 ] print xs That didn't quite compile as-is; I assume you ment: main = do xs - return [ 1, 2, error throw ] `catch` \ any - do putStrLn caught return [ 4, 5, 6 ] print xs When run, I get: Fail: throw In any case, in the more general case, I don't see a problem with that. I get an exception when I try to use something. That's fine. In an imperative program that solves the same problem the same way, you'd see the exception at the same point. ___ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell
Re: [Haskell] Re: Exceptions
John Goerzen wrote: main = do xs - return [ 1, 2, error throw ] `catch` \ any - do putStrLn caught return [ 4, 5, 6 ] print xs When run, I get: Fail: throw In any case, in the more general case, I don't see a problem with that. I get an exception when I try to use something. That's fine. In an imperative program that solves the same problem the same way, you'd see the exception at the same point. No, in a traditional (strict) programming language the program would print caught [4,5,6] The exception throw would not appear in the output. -- Ben ___ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell
Re: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
On Aug 9, 2004, at 5:00 PM, Simon Peyton-Jones wrote: Closed classes are certainly interesting, but a better way to go in this case is to allow the programmer to declear new kinds, as well as new types. This is what Tim Sheard's language Omega lets you do, and I'm considering adding it to GHC. FWIW, Martin Wehr designed a Haskell-like type system with: * not only datakinds, but datasuperkinds, datasupersuperkinds, etc., in short, data-n-types for every finite dimension n, all parametric, * along with parametrically polykinded, polysuperkinded, etc., in short, poly-n-morphic functions, * along with map, simple folds and nested folds for all these things, * not to mention an algorithm for principal type inference in his 1998 paper: @misc{ wehr98higher, author = M. Wehr, title =Higher order algebraic data types, text = Martin Wehr. Higher order algebraic data types (full paper). Technical report, University of Edinburgh, URL http://www.dcs.ed.ac.uk/home/wehr, July 1998., year = 1998, url = citeseer.nj.nec.com/wehr98higher.html } The title of the paper is a bit misleading: higher-dimensional is better than higher-order, as higher-order functions are the chief thing missing from Wehr's system. But these are easily added in the standard fashion, which is to say, only at the value level, and by simply neglecting the problem of defining folds for datatypes involving (-). Two significant differences between Wehr's system and the one Simon described is that every kind in Wehr's system has values, and there is no distinguished kind *. I tried to champion this (very incoherently) in a talk at the Hugs/GHC meeting in, I think, 2000, where Tim also presented some of his early ideas on datakinds. (BTW, given such an expressive system, I think you may find, as I did, that the number of ways to represent what amount to essentially the same type grows ridiculously large, and this is one of the motivations for treating more general notions of type equivalence than equality, like for example canonical isomorphy as I am doing in a forthcoming paper.) There is also an extended abstract of Wehr's paper in CTCS (no citation handy---I'm posting this from at home), and a categorical semantics which is, however, not for the faint of heart: @article{ wehr99higherdimensional, author = Martin Wehr, title =Higher-dimensional syntax, journal = Electronic Notes in Theoretical Computer Science, volume = 29, year = 1999, url = citeseer.nj.nec.com/wehr99higher.html } Eelco Visser also defines a notion of multi-level type system, and gives several examples of how they can be used, in his PhD thesis. One of the examples, as I recall, shows how datakinds and polykinded functions subsume uni-parameter type classes (without overlapping instances). Regards, Frank ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
Just wondering, but what exacly is the problem with this open/closed stuff? As far as I understand it new instances can be added to classes in Haskell (because they are open)... But its not like instances can be added at link time, and all the instances that you wish to be considered _must_ be imported into the current module! [..] Sure it's the case that instances are closed in any given build of a program. But they're not closed over the (maintenance / extension) lifetime of that program. If the compiler treated instances as closed in this way, then adding a new instance to the program could break existing parts of the program. This would be a development nightmare. This is just an example of a general principle in language / compiler design - it's not sufficient that the behaviour be specified, it must behave predictably from the programmer's point of view; in particular, local changes shouldn't have global effect. This also comes up in optimisations - you could write a compiler that recognised occurrences of bubble sort and replaced them with quicksort, for example, but it wouldn't be a good idea, because a small change to the code might cause it to no longer recognise it as bubblesort - with a consequent asymptotic slowdown that bears no relation to the change just made. --KW 8-) ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
If the compiler treated instances as closed in this way, then adding a new instance to the program could break existing parts of the program. But there are many ways to do this... and besides this doesn't really make sense... consider the following: module A defines class X module B imports class X defines some instances module C imports A B Now, you can add any instance you like to module C and it is never seen by modules A and B... In other words even if you assume closed classes you cannot break an existing module without editing that module or one imported by it (in which case you can break _anything_ by deleting a function) If you don't allow overlapping instances none of this makes any difference anyway - closed or open ... (instance selection cannot change if no instances are allowed to overlap) Finally if you allow overlapping instances you can break existing code (without the closed assumption) just by putting in a more specific instance in a module included in some module using the more general instance. Thoughts? What is the problem with assuming all classes are closed within the available context (imported modules) Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
module M where class C a where op :: a - a instance C Int where op x = x+1 f x = Just (op x) Under your proposal, I'd infer f :: Int - Maybe Int, on the grounds that C is closed and there is only one instance. Simon | -Original Message- | From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of MR | K P SCHUPKE | Sent: 12 August 2004 14:03 | To: [EMAIL PROTECTED]; [EMAIL PROTECTED] | Subject: Re: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either] | | Okay anybody whish to argue against these points: | | 1) closed or open is the same if no instances overlap | | 2) overlapping instances (open or closed) can break | code in modules which import the defining module | if a new instance is declared in any imported | module. | | 3) code changes in non-imported modules cannot have | any affect on _this_ module. (And here I count | any module in the import tree as imported - at | least in terms of recompilation dependancies) | | The conclusion appears to be it is overlapping instances | that cause code to be 'breakable' by simply defining a new | instance and not closing the class. Closing the class | would appear to have no adverse affects on existing | programs (as it allows better improvement rules) all existing | programs that compile without the better improvement rules | should still compile - just a few more programs will be | valid with the closed assumption? | | Keean. | ___ | Haskell-Cafe mailing list | [EMAIL PROTECTED] | http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
On 12/08/2004, at 11:05 PM, Simon Peyton-Jones wrote: module M where class C a where op :: a - a instance C Int where op x = x+1 f x = Just (op x) Under your proposal, I'd infer f :: Int - Maybe Int, on the grounds that C is closed and there is only one instance. If I'm reading Keean's posts right, that's exactly his point: if you only have one instance of class C, then it's valid to improve f's type to :: Int - Maybe Int, right? If, on the other hand, you had another instance (e.g. instance C Bool), then the signature of f would have to remain polymorphic. -- % Andre Pang : trust.in.love.to.save ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
but if f is exported, that's probably not what you want. And if you give a type sig to f, f:: forall a. C a = a - Maybe a the type sig will say it's polymorphic, while the improvement rule will say that a must be Int. Simon | -Original Message- | From: André Pang [mailto:[EMAIL PROTECTED] | Sent: 12 August 2004 14:52 | To: Simon Peyton-Jones | Cc: MR K P SCHUPKE; [EMAIL PROTECTED]; [EMAIL PROTECTED] | Subject: Re: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either] | | On 12/08/2004, at 11:05 PM, Simon Peyton-Jones wrote: | | module M where | | class C a where | op :: a - a | | instance C Int where | op x = x+1 | | f x = Just (op x) | | Under your proposal, I'd infer f :: Int - Maybe Int, on the grounds | that C is closed and there is only one instance. | | If I'm reading Keean's posts right, that's exactly his point: if you | only have one instance of class C, then it's valid to improve f's type | to :: Int - Maybe Int, right? | | If, on the other hand, you had another instance (e.g. instance C Bool), | then the signature of f would have to remain polymorphic. | | | -- | % Andre Pang : trust.in.love.to.save ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
Hi On Mon, 9 Aug 2004, Simon Peyton-Jones wrote: Closed classes are certainly interesting, but a better way to go in this case is to allow the programmer to declear new kinds, as well as new types. This is what Tim Sheard's language Omega lets you do, and I'm considering adding it to GHC. kind HNat = HZero | HSucc HNat class HNatC (a::HNat) instance HNatC HZero instance HNatC n = HNatC (HSucc n) [..] At the moment I'm only thinking of parameter-less kind declarations but one could easily imagine kind parameters, and soon we'll have kind polymorphism but one step at a time. Any thoughts? Yes. Yes please. This is still in the realm of using type-level proxies for values, but it's a much more practical fake of dependent types than you can manage with type classes. It lets you do quite a lot of the handy indexing that's found in basic dependently typed programs, without quite crossing the line to full-on value dependency. Of course, I recommend crossing this line in the long run, but I accept that doing so might well mess things up for GHC. This is a sensible, plausible step in the right direction. And I'm sure you'll be able to jack it up to datakinds parametrized by datakinds, provided all the indices are in constructor form: the unification apparatus you've already got for datatype families (or GADTs as you've dubbed them) should do everything you need, as long as you unify the indices before you unify the indexed `values'. What you still don't get is the ability to use datatype families to reflect on values in order to extend pattern matching, the way James McKinna and I do in `The view from the left'. But one step at a time. You also don't get for free the ability to write type-level programs over these things. If you do add type-level programs over datakinds, you may find that the unification-in-the-typing-rules style comes under strain. I'm told that's why the ALF crew retreated from full-on datatype families, which is why they're not in Cayenne. The Epigram approach is different: the unification problems are solved internally to the theory, so you still get the solutions when they're easy, but the wheels don't come off when they're not. Even so, you do get quite a long way towards the `static' uses of dependent types which support n-ary vectors and the like, but where n isn't supposed to be a run-time value. You'll get `projection from a vector is exception-free'; you won't get `projection from a vector returns the thing in that position in the vector'. So let's have this, and we'll see how many of the programs I've written in the last 5+ years with these data structures become Haskell programs. More than a few, I expect. Cheers Conor ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
kind statements sound like a good idea - a couple of questions spring to mind - what is the parameter of a kind statement (a type or a kind... a kind makes more sense) ... do we have to stop with kinds what about kinds of kinds - the statement has identical syntax to a data declaration, is there no way to combine the two, with perhaps a level value? An example of where you may need kinds-of-kinds (etc) is consider peano numbers (declared as a kind) ... now consider we have several implementations (unary - binary etc) which we wish to group together as an equivalent to the Num class. I accept the above is not a good example as this is better served by a class (as you may well want it 'open')... It seems from a theoretical point of view easy to add multiple levels of kinds instead of one level... of course it is probably much more difficault to do this to GHC? Of course with kinds of kinds you either have to annotate the statement with the level - or let the compiler infer the level. The latter seems much more difficault as the same 'level-less-kind statement' could be used on multiple levels... Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
Is there any possibility of a theory that will avoid the need to replicate features at higher and higher levels? If we consider types, types are a collection of values, for example we could consider Int to be: data Int = One | Two | Three | Four ... Okay, so the values in an integer are connected by functions (operations like plus, multiply) - but we can consider these externally defined (as they are primitives). Also we have special symbols for the values of this type (type constructors are 'values') like 1, 2, 3... etc. Likewise Char is effectively an enumeration of ascii values (or Unicode values). All these standard types behave like enumerations of values. Lists are effectively nested binary products: data List a = Cons a (List a) But I digress, my point is that types are a 'set' of values, and so we can consider the simplest example: data Bool = True | False So bool is a type and True and False are the values 'in' that type - _but_ we can also write: kind Bool = True | False Where Bool is a kind and True and False are values, Kinds are really a different name for type_of_types... and this continues upwards forever (a type_of_type_of_types) ... we can effectively flatten this by considering types as values and kinds as types. What is the difference between a type and a value? So we can consider: data Bool = True | False to be a relationship between elements at the Nth level (the RHS) and the N+1th level (the LHS). This relationship could be applied at any level, and it should be possible to detemine the level at which we wish to apply this relationship by the context in which it is used. Imagine a function 'not': not :: Bool - Bool not True = False not False = True we could apply this at the values level: not True False The type level: class (Bool a,Bool b) = Not a b | a - b instance True False instance False True How does this differ from the original functional form? can we imagine doing: not' :: Not a b = a - b but perhaps writing it: not' :: Bool a = a - not a After all is not 'not' a complete function from Bool to Bool, whatever Bool is? (Bool could be a type or a type of types, or a type of type of types...) Would this not result in greater code re-use? Perhaps classes would become redundant (apart from being used to allow overloading...) My theory here is a bit shakey - but the name Martin Lof springs to mind. Keean. *errata - when I said Where Bool is a kind and True and False are values I of course meant w true and false are types! ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
Simon Peyton-Jones wrote: kind HNat = HZero | HSucc HNat class HNatC (a::HNat) instance HNatC HZero instance HNatC n = HNatC (HSucc n) There is no way to construct a value of type HZero, or (HSucc HZero); these are simply phantom types. ... A merit of declaring a kind is that the kind is closed -- the only types of kind HNat are HZero, HSucc HZero, and so on. So the class doesn't need to be closed; no one can add new instances to HNatC because they don't have any more types of kind HNat. With the flag -fallow-overlapping-instances, could I still add an instance instance HNatC n = HNatC (HSucc (HSucc n)) or instance HNatC (HSucc HZero) ?? If I may I'd like to second the proposal for closed classes. In some sense, we already have them -- well, semi-closed. Functional dependencies is the way to close the world somewhat. If we have a class class Foo x y | x - y instance Foo Int Bool we are positive there may not be any instance 'Foo Int anything' ever again, open world and -fallow-overlapping-instances notwithstanding. In fact, it is because we are so positive about that fact that we may conclude that Foo Int x implies x = Bool. At least in the case of functional dependencies, the way to close the world gives us type improvement rules. One might wonder if there are other ways to (semi-)close the world with similar nice properties. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: exceptions vs. Either
After all, Java basically does exactly what you're asking for with Java's head/tail would be doing runtime checks if they are throwing exceptions, static guarantees mean the program would not be allowed to compile if it broke the static guarantees. end-programmers have to worry much less about handling errors properly. Which is a bad thing! All programmers always have to consider error conditions, if they don't they write buggy code - that's the nature of the beast. I prefer making programmers expicitly face the decisions they are making, rather than have things implicitly handled in a way that hides what is going on from the programmer. Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
After all, Java basically does exactly what you're asking for with Java's head/tail would be doing runtime checks if they are throwing exceptions, static guarantees mean the program would not be allowed to compile if it broke the static guarantees. Not so. In Java, the programmer is forced to handle most exceptions by the type system. That is, if the exception is not handled, the program will not compile, thus providing a static guarantee that exceptions are handled. Only unchecked exceptions (RuntimeException and Error) are exempt from this check. --KW 8-) ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
Not so. In Java, the programmer is forced to handle most exceptions I forgot you had to do that... Exceptions are explicit in the type signatures. I think Oleg posted a message a while back about how to make exceptions explicit in haskell type signatures... But I would rather use static guarantees where possible, and exceptions where necessary. I haven't really tried using the techniques for explicit exceptions, but on consideration I might see if it is practical to code in that style... Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
Static guarantees are great, but if you have to explicitly change your style of coding to cope with those extra constraints, it can become (very) cumbersome. I had to change coding style moving from imperative to declarative languages, but I think it was worth it... Likewise I think having the ability to make strong static guaranees is worth it - you may not, which is why it is important not to break any existing programs with language extensions (if any are necessary). My programs will have less bugs though! worse-is-better, even in its strawman form, has better survival I fully subscribe to the 'worse is better' approach, but I don't see how it contradicts the principle of static guarantees - you can have both. Simplicity is about algorithmic complexity not about whether type signatures are provided by the programmer. Infact type signatures are in themselves an embodyment of the simple is better principle. A type signature expresses certain static guarantees about the function in a vary compact way. Consider the sort example... being able to declare a type signature on a sort algorith that enforces ordering of the output would prove the sort algorithm can _only_ output correctly sorted lists under _all_ circunstances. This type signature is much simpler than the actual sort - hence is useful. sort :: (HList l,HOrderedList l') = l - l' Nice and readable, and much simpler than the actual algorithm (be it bubble sort, or a quick sort) Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
correctly sorted lists under _all_ circunstances. This type signature is much simpler than the actual sort - hence is useful. sort :: (HList l,HOrderedList l') = l - l' Nice and readable, and much simpler than the actual algorithm (be it bubble sort, or a quick sort) The type signature you give is no different from sort :: (C1 l, C2 l') = l - l' and conveys no more information. You should include the definitions of the classes before saying this is much simpler than the actual sort. --KW 8-) ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
You should include the definitions of the classes before saying HOrderedList l' just has to prove by induction that for any element in the list, the next element is greater, so the class is simply: class HOrderedList l instance HNil instance HCons a HNil instance (HOrderedList (HCons b l),HLe a b) = HCons a (HCons b l) which is the equivalent type level program to ordered :: [Int] - Bool ordered [] = True ordered [a] = True ordered (a:(b:l)) = if a=b then ordered (b:l) else False ordered _ = False It is obvious by observation that the a=b ensures order. This is a lot simpler than say a heap-sort. I suppose you could contend that there are some classes above I still haven't defined - but you wouldn't expect to see definitions for (=) which is defined in the prelude. Of course to show statically that order is preserved the 'value' of the elements to be ordered must be visible to the type system - so the values must be reified to types... This can be done for any Haskell type, but for numbers we would use Peano numbers - the HLe class for these is again easily defined by induction: class HLe n n' instance HLe HZero HZero instance HLe x y = HLe (HSucc x) (HSucc y) Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
On Fri, 2004-08-06 at 14:05, MR K P SCHUPKE wrote: You should include the definitions of the classes before saying HOrderedList l' just has to prove by induction that for any element in the list, the next element is greater, so the class is simply: class HOrderedList l instance HNil instance HCons a HNil instance (HOrderedList (HCons b l),HLe a b) = HCons a (HCons b l) Somewhat off-topic, It's when we write classes like these that closed classes would be really useful. You really don't want people to add extra instances to this class, it'd really mess up your proofs! I come across this occasionally, like when modelling external type systems. For example the Win32 registry or GConf have a simple type system, you can store a fixed number of different primitive types and in the case of GConf, pairs and lists of these primitive types. This can be modelled with a couple type classes and a bunch of instances. However this type system is not extensible so it'd be nice if code outside the defining module could not interfere with it. The class being closed might also allow fewer dictionaries and so better run time performance. It would also have an effect on overlapping instances. In my GConf example you can in particular store Strings and lists of any primitive type. But now these two overlap because a String is a list. However these don't really overlap because Char is not one of the primitive types so we could never get instances of String in two different ways. But because the class is open the compiler can't see that, someone could always add an instance for Char in another module. If the class were closed they couldn't and the compiler could look at all the instances in deciding if any of them overlap. So here's my wishlist item: closed class GConfValue v where ... Duncan ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: exceptions vs. Either
On 06/08/2004, at 6:56 PM, MR K P SCHUPKE wrote: After all, Java basically does exactly what you're asking for with Java's head/tail would be doing runtime checks if they are throwing exceptions, static guarantees mean the program would not be allowed to compile if it broke the static guarantees. As Keith said, Java will check at compile time whether or not you handle the exception. My point is this: it is impossible to check whether the exception is properly handled. If you adjust Haskell's tail function to return (Maybe [a]) instead of just ([a]), you are doing the thing as Java from a pragmatic perspective: you are adding information to the type system that tells the programmer the function may fail. You also suffer the same consequence as Java: you have no idea whether the programmer properly handles the error situation. If I am writing a one-shot, never-use-again script that takes 3 minutes to write, and I _know_ that I'm not going to be feeding the tail function a non-empty list--e.g. because I'm writing a one-shot five-minute script to transform a file from one text format to another, as is the case for lots of Perl programs--then the extra Maybe type just gets in the way. I'll either ignore the Nothing case, or write `case tail foo of ... Nothing - error bleh'. I will go so far to say that such a program can be considered correct: it does exactly what I want it to do, in exactly the circumstances I desire (0 byte files being specifically excluded from the circumstances!). Which is a bad thing! All programmers always have to consider error conditions, if they don't they write buggy code - that's the nature of the beast. I prefer making programmers expicitly face the decisions they are making, rather than have things implicitly handled in a way that hides what is going on from the programmer. It's a question of whether the library designer should impose their will on the library user. As a library designer, do you feel that you are always making the right decision for the library user 100% of the time? I know I never feel like that when I write libraries ... -- % Andre Pang : trust.in.love.to.save ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: exceptions vs. Either
What can I say... Static typing guarantees is what made me switch from object oriented languages like C++/ObjectiveC/Java (that and the availability of a good compiler) - So I am obviously in favour of more static guarantees - I believe programming by contract is the way to reliable _engineered_ software, so the more contractual obligations that can be concisely and clearly expressed in the type system the better. I think Haskell should support dependant types, after all if you don't want to use them you don't have to... (backwards compatability and all that) although it would be useful to have a replacement prelude that used dependant types versions of head etc... I have a hard time understanding why functional programmers would not want more static typing guarantees, after all they can always use C if they dont like type systems! Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
I have a hard time understanding why functional programmers would not want more static typing guarantees, after all they can always use C if they dont like type systems! I think the value of Haskell's type system is that it catches a lot of bugs _cheaply_. That is, with little programmer effort to write the type annotations, little effort to understand them and little effort to maintain them as the program evolves. In general, as we try to express more and more in the type system, the costs go up so the goal in using a different programming style or extending the type system is to catch more bugs without significantly increasing the cost. At an extreme, we could probably use some variant of higher order predicate calculus as our type system and specify that a sort function (say) returns an ordered list but the programmer effort in doing so would probably be quite high. Using Maybe and the like to catch more errors with the type system isn't as expensive as using full-on specification and is often the right thing to do but, in some cases, the benefits of programming that way don't justify the effort required whereas the techniques suggested for reporting the location of the problem are cheap and effective. -- Alastair Reid ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
Hi folks Alastair Reid wrote: I have a hard time understanding why functional programmers would not want more static typing guarantees, after all they can always use C if they dont like type systems! I think the value of Haskell's type system is that it catches a lot of bugs _cheaply_. That is, with little programmer effort to write the type annotations, little effort to understand them and little effort to maintain them as the program evolves. I entirely agree. The effort-to-reward ratio is the key consideration when deciding how much information to build into types. I'm biased, of course, but the thing I like about working with a dependent type system is that I at least have the option to explore a continuum of static expressivity and choose a compromise which suits my conception of the task at hand. To oppose the availability of more informative types is to rule out the option of exploiting them even when they are useful. A key aspect of the Epigram approach is to pay attention to the /tools/ we use to write programs. Of course it gets harder to work with more involved types if you have to do it all in your head. Frankly, polymorphic recursion is enough to make my brain hurt, let alone the kind of type class shenanigans which various people (myself included) have been engaging in. I have a not-so-secret weapon. Once I've developed the program interactively in Epigram (or its predecessor, OLEG, in the case of the Faking It stuff), exploiting the fact that a computer can push type information /into/ the programming process, it's quite easy to crank out the relevant steaming lump of obfuscated Haskell. One tool that's really needed here is a refactoring editor which enables you to write the program naively, exposing the exceptional cases, then, once you've seen where they show up, refine the data structures to eliminate some or all of them. It's a fair cop: at the moment, Epigram requires you to dream up the data structures from thin air before you write the programs. That doesn't fit with the fact that good ideas only tend to come a bit at a time. However, we can certainly improve on this situtation, given time and effort. In general, as we try to express more and more in the type system, the costs go up so the goal in using a different programming style or extending the type system is to catch more bugs without significantly increasing the cost. At an extreme, we could probably use some variant of higher order predicate calculus as our type system I'd recommend a system which enables you to build stronger structural invariants directly into data structures, rather than using data types which are too big, and then a whole pile of predicates to cut them down to size afterwards: as you fear, the latter is a good way to fill a program up with noisy proofs. But if you use indexed datatype families (Dybjer, 1991), you can avoid a lot of this mess and what's more, a type-aware editor can rule out many exceptional cases on your behalf. In many cases, the Epigram editor shows you exactly the data which are consistent with the invariants, without any effort on your part. and specify that a sort function (say) returns an ordered list but the programmer effort in doing so would probably be quite high. I'm pleased to say it isn't. See http://www.dur.ac.uk/c.t.mcbride/a-case/ and in particular http://www.dur.ac.uk/c.t.mcbride/a-case/16so/ onwards, for treesort-enforcing-sorted-output. There's not much to it, really. The thing is, ordinary if-then-else loses the fact that performing the comparisons directly establishes the properties required to satisfy the sorting invariants in the output structures. It's not hard to remedy this situation. Moreover, most of the logical plumbing can be managed implicitly, as it merely requires picking up proofs which are immediately visible in the context. Of course, (see Inductive Families Need Not Store Their Indices, by Edwin Brady, James McKinna and myself) none of this logical stuff incurs any run-time overhead. Using Maybe and the like to catch more errors with the type system isn't as expensive as using full-on specification and is often the right thing to do but, in some cases, the benefits of programming that way don't justify the effort required whereas the techniques suggested for reporting the location of the problem are cheap and effective. You're absolutely right. And today's technology often means that the match-exception way, let alone the Maybe-way, wins this trade-off. Tomorrow's technology will not remove this trade-off, but it might sometimes change the outcome of the calculation. I think that's worth working for. Cheers Conor ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: exceptions vs. Either
specify that a sort function (say) returns an ordered list Fistly this is already possible in Haskell - see Oleg, Ralf and My paper: @misc{KLS04, author = Oleg Kiselyov and Ralf L{\a}mmel and Keean Schupke, title = {Strongly typed heterogeneous collections}, booktitle = {ACM SIGPLAN Workshop on Haskell}, year = 2004, month = sep, publisher = ACM Press, notes = To appear } http://homepages.cwi.nl/~ralf/HList/ Here we show constrained heterogeneous lists (but you can also constrain the type in the list to turn it into a homogeneous list) that includes the ability to impose ordering constraints... Writing the constraint classes is a little involved but using it is as simple as: sort :: (HList l,HOrderedList l') = l - l' and supposing you dont like having to write the complete type, you can use: ordered :: HOrderedList l = l - l ordered = id then you can use this in a function with no type given: sort = ordered . sort' So my point is this can be added to haskell _without_ breaking any existing functionality - it is not either/or it is both/and! Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: exceptions vs. Either
On 05/08/2004, at 7:40 PM, MR K P SCHUPKE wrote: I have a hard time understanding why functional programmers would not want more static typing guarantees, after all they can always use C if they dont like type systems! Static guarantees are great, but if you have to explicitly change your style of coding to cope with those extra constraints, it can become (very) cumbersome. After all, Java basically does exactly what you're asking for with head/tail: if you were to write a tail method in a List class, you could simply throw a EmptyListException. That's really the same effect as tail in Haskell returning a Maybe: both forms force you to perform error-handling in the calling function. However, I think Java has shown that forcing error-handling on the caller via exceptions is no magic bullet: a lazy programmer will simply catch the exception in an empty catch {} block. It's a human problem, not a technical one. Obviously exceptions, Maybes, monads etc. are useful, but forcing the programmer to Do The Right Thing is nearly impossible. I personally think that using tricks such as type classes to propagate constraints and errors via the type system is a fantastic idea, because then end-programmers have to worry much less about handling errors properly. -- % Andre Pang : trust.in.love.to.save ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: exceptions vs. Either
On 04/08/2004, at 12:28 AM, MR K P SCHUPKE wrote: f (case xs of (x:_) - x; [] - error whoops) -- direct style Yup, this is how I do it... I never use head! I like to pass failures back up to the level where some kind of sensible error message can be generated. In your example the error is no better than with 'head' - the point is a Nothing can be 'caught' outside of an IO monad. I would suggest using the type system as I said earlier so: toNonEmptyList :: [a] - Maybe (NonEmpty a) toNonEmptyList (a0:_) = Just (NonEmpty a) toNonEmptyList _ = Nothing Then redefine head: head :: NonEmpty a - a head (NonEmpty (a0:_)) = a0 There's an interesting discussion going on at Lambda the Ultimate right now, about this very topic: http://lambda-the-ultimate.org/node/view/157#comment There are plenty of noteworthy comments there, but one which quite nicely expresses my point of view is: Using Maybe for this is like saying - let's turn this partial function into a total one by lifting its range to include Nothing. It became total by obtaining permission to return something I have no use of. I do not say monads are not useful, or Maybe is not useful. And, of course, there's the type wizardry post by Oleg: http://lambda-the-ultimate.org/node/view/157#comment-1043 I'd like to point out that it is possible in Haskell98 to write non-trivial list-processing programs that are statically assured of never throwing a `null list' exception. That is, tail and head are guaranteed by the type system to be applied only to non-empty lists. Again, the guarantee is static, and it is available in Haskell98. Because of that guarantee, one may use implementations of head and tail that don't do any checks. Therefore, it is possible to achieve both safety and efficiency. Please see the second half of the following message: http://www.haskell.org/pipermail/haskell/2004-June/014271.html -- % Andre Pang : trust.in.love.to.save ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: exceptions
just out of curiosity, which is the proper idiom? trace a = r - catch a (\e - putStr exceptional\n throw e) trace a = r - catch a (\e - putStr exceptional\n ioError e) I am worried that one might subtly change the semantics of an execption depending on how it was originally thrown... but i am uncertain how. probably some obscure case involving bottoms. But to be on the safe side, thought I'd ask. The second is more correct, if you're using Prelude.catch. If you're using Control.Exception.catch, then you want throwIO instead of ioError. The difference between throw and ioError is this: seq (throw e) E == throw e seq (ioError e) E == E Cheers, Simon ___ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell
Re: Exceptions with Hugs 98
juan albornoz [EMAIL PROTECTED] writes: Hello I need to catch some exceptions which I've thrown with the error function. I am using WinHugs 98. Can any one help me? Something along the lines of: import qualified Exception as E foo = E.catch mumble (\e - print e) should do you. See the papers on exception handling: http://www.reid-consulting-uk.ltd.uk/alastair/publications/except-pldi.ps.gz and the documentation for the library http://www.haskell.org/ghc/docs/latest/html/base/Control.Exception.html (This is GHC documentation but the Hugs library is mostly compatible. The main differences are in things not covered by the original paper - see the (quite readable) source code for the Hugs version of Exception for details.) -- Alastair Reid [EMAIL PROTECTED] Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/ ___ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell
Re: Exceptions and sockets
In local.glasgow-haskell-users, you wrote: I `Socket.accept' a connection and get a handle. Now, if the connections dies, but I still write to the handle, the whole thing crashes - without throwing an exception. (I checked that the RTS thinks `hIsWritable h' even if `h' does no longer exist in reality.) Looks to me like you're forgetting that the OS will give you a sigPIPE on (semi-)closed sockets, which translates to a segfault unless you install a signal handler: - http://www.haskell.org/ghc/docs/latest/set/socket.html#AEN13989 - socket(2) -- Abstrakte Syntaxträume. Volker Stolz * [EMAIL PROTECTED] * PGP + S/MIME ___ Glasgow-haskell-users mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Exceptions
I just installed ghc-4.08.1 on a Win2k computer. Then I tried to compile a program that uses exceptions. Compilation succeeds, but running the program causes a "Application Error" dialog to appear. It says "The application failed to initialize properly (0xc005). Click on OK to terminate the application.". Here is the Haskell program: -- module Main(main) where import Exception main = catchAllIO (putStrLn (head ([] :: [String]))) (\e - putStrLn "hi" ) -- Compiled it with ghc-4.08.1 -i/apps/ghc/lib/imports/lang -syslib lang Main.hs This should be OK, but as Christian Lescher says, you can just say "-package lang" rather than all the gumph you have. The error message looks like a fairly typical Windows RTS problem. It could be that I just missed something in my recent round of fixes. I'm afraid I'm away at a DevLab in Redmond at the moment, so I'll check it next week when I return. The only thing I can suggest for now is to reinstall GHC unless you really have *just* installed it; I updated the InstallShield on about November 6th. -- http://sc3d.org/rrt/ | competent, a. underpromoted ___ Glasgow-haskell-bugs mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs
Re: Exceptions and functions. Second round?
On 16-Jun-1998, Greg Michaelson [EMAIL PROTECTED] wrote: In SML(sorry...), in: exp1 handle exp2 exp1 is evaluated and any exception raised within it is handled by an appropriate handler in exp2. If there isn't one then the exception is handled in the enclosing environment for the whole construct. Similarly, if an exception is raised in exp2 then it is handled in the enclosing environment. So exception handling follows a stack based regime. Instead, how about having exp2 handle exceptions raised within itself You can do this quite easily: expr1 `recursive_handle` expr2 = expr1 `handle` (\ e - expr2 e `recursive_handle` expr2) (Here I'm using a version of `handle' where the handler is the 2nd argument.) However, doing this might be a bad idea, I think, because if `expr2' raises any exception, this could easily lead to an infinite loop. and also allowing mutually recursive handlers? This is also allowed by the current proposal -- it's quite straightforward: handler1 e = stuff1 `handle` handler2 handler2 e = stuff2 `handle` handler1 -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions are too return values!
On 15-Jun-1998, Peter White peter@galois wrote: On June 15, Fergus Henderson writes As noted earlier, things like heap overflow, and stack overflow are different from other kinds of exceptions. They can't be modelled using the domain-theoretic semantics. Rather, they reflect the failure of the operational semantics to accurately reflect the domain-theoretic semantics. Thus the treatment of these exceptions may need to be different to the treatment of ordinary exceptions. One of the advertisements of Haskell is that you can reason about your program, by performing mathematical proofs about the program. Haskell has gone a long way to incorporating IO and stateful computations in such a way that you still get referential transparency, and you can still reason about programs. If the operational semantics fails to reflect, the domain-theoretic semantics, then it would appear that the ability to reason about the programs dissappears. This is not the case here. The reason is that although the operational semantics are not complete w.r.t. the denotational (domain-theoretic) semantics, they are sound. That is, you can't use the denotational semantics to prove that your program won't get a heap overflow; but you can use them to prove that if your program doesn't get a resource failure like that, then it will compute the right answer. If you want to reason about resource limits, then you need to use an operational semantics, not the denotational semantics. I think it is a requirement upon a Haskell implementation to preserve the independence of threads by "localizing" the resources to the threads, such that each thread can predict by itself, independently of any other thread, whether its resources will be sufficient. I don't think this is desirable in the general case. I think it would be useful to *allow* threads to reserve resources, but often it is difficult to predict in advance exactly how much each thread will use, and frequently it is better to deal with resource failures when they arise. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions are too return values!
On 15-Jun-1998, Fergus Henderson [EMAIL PROTECTED] wrote: On 12-Jun-1998, Scott Turner [EMAIL PROTECTED] wrote: At 14:40 1998-06-10 +0100, you wrote: Here's a reasonable design for exceptions in Haskell: * handle :: (String - IO a) - IO a - IO a You probably realized more quickly than I how this can leak exceptions. ... Is this considered a drawback? This kind of exception handling can "leak" exceptions, but not in the way you described. ... What I mean is main = do quotient - handle (const (return 0)) (return (0 / 0) -- Looks plausible -- but the exception isn't raised yet. print quotient -- Here the expression 0/0 is evaluated -- and the exception is raised with no handler. This is not correct. This example would print out `0' rather than raising an uncaught division by zero exception. I'm afraid I must retract those statements. Scott Turner was quite correct, and I was mistaken. My apologies! As Scott pointed out to me in personal email, SLPJ's definition of `handle' | * handle :: (String - IO a) - IO a - IO a | (handle h a) tries to perform the action a. | If doing so delivers a set of exceptional values then | apply the exception handler h to the string that names | one of them. It is not defined which of the exceptional | values is picked. means that it only catches exceptional values in the I/O action, not exceptional values in the return value. Regarding Scott's question Is this considered a drawback? my answer is still much the same -- yes, it's a drawback, but I'd place the blame more on laziness than exception handling. I consider it only a minor drawback, since the "leakage" can be avoided if you use a version of `handle' which is strict in the return value, e.g. strict_handle handler action = handle handler strict_action where strict_action = do value - action seq value return value -- or with `hyper_seq' instead of `seq' -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions and functions. Second round?
At 14:01 +0200 98/06/16, Jerzy Karczmarczuk wrote: There is - in my opinion - nothing "exceptional" in *standard* imperative control structures. OK, I admit that I am a sinner myself, and I had written some small event-driven programs using (ppardonnez le mot) "longjump" in C, but I believe strongly that the functional programming community with the monadic religion etc. is much more close to the Truth. I think you are raising the issue of exceptions without handling it properly. :-) Of course, when leaping to the next level of abstraction, exceptions are as exceptional as irrational numbers are irrational and the imaginary complex unit is imaginary in the original linguistic semantic sense, that is, not at all. And C is a language whose trademark is the absence of logical structure described by theory, so what's done in that language is rather misleading if one wants to produce a new generation of languages admitting new abstractions for the convenience to people that can think in terms of abstractions and by that do more efficient work. In fact, the opposite seems to happen, the new concept of exceptions seems to fit those old imperative ad hoc structures into a nice single abstraction. As we sit here with a language, Haskell, which normally does not admit imperative structures, using exceptions might be a neat way to do it. Hans Aberg * Email: Hans Aberg mailto:[EMAIL PROTECTED] * Home Page: http://www.matematik.su.se/~haberg/ * AMS member listing: http://www.ams.org/cml/
Re: Exceptions are more than just return values!
At 12:15 +0100 98/06/16, Tony Finch wrote: I like the idea of the non-deterministic exception system that has been proposed, but it'll be interesting to see how well it works in real life... Perhaps one can have them both with handlers handle_absolutely and handle_maybe, so that user can play around with them both and compare. Hans Aberg * Email: Hans Aberg mailto:[EMAIL PROTECTED] * Home Page: http://www.matematik.su.se/~haberg/ * AMS member listing: http://www.ams.org/cml/
Re: Exceptions and functions. Second round?
[EMAIL PROTECTED] (Greg Michaelson) writes: In SML(sorry...), in: exp1 handle exp2 exp1 is evaluated and any exception raised within it is handled by an appropriate handler in exp2. If there isn't one then the exception is handled in the enclosing environment for the whole construct. Similarly, if an exception is raised in exp2 then it is handled in the enclosing environment. So exception handling follows a stack based regime. Instead, how about having exp2 handle exceptions raised within itself and also allowing mutually recursive handlers? No need to wire this into the semantics/implementation - it's easy to implement your "recursive catch" using the plain catch: rcatch :: IO a - (IOError - IO a) - IO a m `rcatch` h = m `catch` \err - (h err `catch` h) but notice that I call plain catch when invoking the handler - so we won't get into infinite loops. If infinite loops are what you want :-), then we have "really recursive catch" rrcatch :: IO a - (IOError - IO a) - IO a m `rrcatch` h = m `catch` \err - (h err `rrcatch` h) Yet another variant would be to invoke the handler up to 10 times (say) before giving up and invoking the outer handler. This might form a very nice inter-process communication mechanism, which may be recognised by people who have written interrupt driven multi-process systems. I sort of see how this might work - but I've only _read about_ such systems - not actually implemented them. Where could I find out more? Alastair ps I'm getting kinda worried about people talking about using the exception handling mechanism to implement various control constructs. While our non-deterministic semantics is (I hope) fine for writing robust programs, I can't see how to build __predictable__ control constructs on top of it and I have no desire to modify the implementation and semantics just so that you can do so. I also think we need to spend some time playing with the GHC-based implementation before we'll really know how it performs. Just how big a problem will it be if an optimiser transforms a program that would have raised an error into a program that enters an infinite loop and triggers a timeout? In some applications, this won't be a problem but it could be a real problem in others.
Re: Exceptions and functions. Second round?
In SML(sorry...), in: exp1 handle exp2 exp1 is evaluated and any exception raised within it is handled by an appropriate handler in exp2. If there isn't one then the exception is handled in the enclosing environment for the whole construct. Similarly, if an exception is raised in exp2 then it is handled in the enclosing environment. So exception handling follows a stack based regime. Instead, how about having exp2 handle exceptions raised within itself and also allowing mutually recursive handlers? This might form a very nice inter-process communication mechanism, which may be recognised by people who have written interrupt driven multi-process systems. It probably has horrendous semantic implications... Greg Michaelson
Re: Exceptions are too return values!
On 12-Jun-1998, Scott Turner [EMAIL PROTECTED] wrote: At 14:40 1998-06-10 +0100, you wrote: Here's a reasonable design for exceptions in Haskell: * handle :: (String - IO a) - IO a - IO a You probably realized more quickly than I how this can leak exceptions. ... Is this considered a drawback? This kind of exception handling can "leak" exceptions, but not in the way you described. Yes, this is a drawback, but it's not nearly as big a drawback it would be if exceptions could leak in the way you were talking about. Furthermore, the leakage seems to be inherent to lazy evaluation, so I'd consider it a drawback of lazy evaluation rather than a drawback of exception handling. The user can avoid such leakage, so long as they're willing to lose some laziness. Details below. What I mean is main = do quotient - handle (const (return 0)) (return (0 / 0) -- Looks plausible -- but the exception isn't raised yet. print quotient -- Here the expression 0/0 is evaluated -- and the exception is raised with no handler. This is not correct. This example would print out `0' rather than raising an uncaught division by zero exception. The reason is basically that the handler is established lazily too. When `print' evaluates its argument, first the handler is established, then 0/0 is evaluated, then the handler catches the exception and returns 0. This may not have been obvious from SLPJ's original description, but if your consider the domain-theoretic semantics, it has to be this way. SLPJ's original description was as follows: | * handle :: (String - IO a) - IO a - IO a | (handle h a) tries to perform the action a. | If doing so delivers a set of exceptional values then | apply the exception handler h to the string that names | one of them. It is not defined which of the exceptional | values is picked. The result of performing the action `return (0 / 0)' is a (singleton) set of exceptional values, so the effect of `handle (const (return 0)) (return (0 / 0))' must be to apply `const (return 0)' to one of those values, which in turn has the same effect as `return 0'. The fact that this is all evaluated lazily doesn't change the semantics. If you want to understand the operational semantics in more detail, then it may perhaps be clearer if you look at my implementation of his `handle' using `ndset_handle' and `ndset_choose', since that breaks things up into smaller pieces, seperating out the exception handling from the nondeterministic choice. But probably the simplest way of seeing it is to look at the domain-theoretic semantics as outlined above. So, your example is not a problem. However, it is true that this kind of exception handling does in a certain sense "leak" exceptions. This is because `handle' only catches exceptions that occur during the evaluation of the top level of the value, it doesn't catch exceptions that occur duing evaluation of the sub-components. For instance, if we just modify your example slightly, then we get an example where exceptions really do "leak" out: main = do list - handle (const (return [])) (return [0 / 0]) print list This example will print "[" and then throw an uncaught division by zero exception. In order to avoid this, the user needs to force strict main = do list - handle (const (return [])) (return e) `hyperseq` e where e = return [0 / 0] print list Here `hyperseq' is a function that is like `seq' except that it forces complete evaluation, not just evaluation to WHNF (weak head normal form). class HyperEval a where hyperstrict :: (a - b) - a - b hyperseq :: a - b - b hyperstrict f x = x `hyperseq` f x instance HyperEval a = HyperEval [a] where [] `hyperseq` val = val (x:xs) `hyperseq` val = x `hyperseq` (xs `hyperseq` val) If we use a version of `handle' where the handler is the second argument rather than the first (a good idea, IMHO!), then the example could be written slighly more elegantly, using `hyperstrict' rather than `hyperseq', as either main = do list - hyperstrict handle (return [0/0]) (const (return [])) print list or if you prefer main = do list - (return [0/0]) `hyperstrict handle` (const (return [])) print list P.S. Is there any reason why something like `HyperEval' isn't built in to Haskell, or at least include in the Haskell Library report? Is there any implementation-specific precedent for something like this in say ghc? -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions are too return values!
On 13-Jun-1998, Peter White peter@galois wrote: I wonder if there is another issue relating potential nondeterminism of exceptions to the independence of threads. It is supposed to be the case that two different threads have behavioral independence, so that an implementation could run the threads in any order, interleave their execution in any way, and the two threads would still give the same results. Well, that depends on whether you want parallelism, or concurrency. If you just want parallelism, i.e. you're just using threads to improve performance, then yes, the order of interleaving should not affect the results. But if you're using concurrency, then this isn't necessarily true -- the order of interleaving may affect the results, and this may be exactly what you want. Take the case of a heap overflow exception. As noted earlier, things like heap overflow, and stack overflow are different from other kinds of exceptions. They can't be modelled using the domain-theoretic semantics. Rather, they reflect the failure of the operational semantics to accurately reflect the domain-theoretic semantics. Thus the treatment of these exceptions may need to be different to the treatment of ordinary exceptions. In particular, instead of data MaybeException a = OK a | GotException (NDSet Exception) ndset_catch :: a - MaybeException a you need something like data ResourceFailure = StackOverFlow | HeapOverFlow | ... data MaybeResourceFailure a = Computed (MaybeException a) | Failed ResourceFailure ndset_catch_all :: a - NDSet MaybeFailure Timeouts may also be considered as resource failures. Interrupt handlers could also be considered as exceptions or resource failures, but I think is probably nicer to consider them as forms of concurrency. Suppose the two threads demand more memory than is provided in the computer. One of the two threads will hit a heap overflow exception. In order to have the implementation guarantee thread independence, the heap overflow of one thread cannot depend upon the memory consumption of the other thread. If there is a dependence, then one thread can determine the behaviour of the other thread by choosing to consume memory on the heap or not. If you're worried about covert communication channels, then yes, you have to worry about things like this. But generally covert communication channels are not an issue. So in general it's enough to say that whether or not you get a HeapOverflow resource failure is nondeterministic. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions are too return values!
On Mon, 15 Jun 1998, Fergus Henderson wrote: P.S. Is there any reason why something like `HyperEval' isn't built in to Haskell, or at least include in the Haskell Library report? Is there any implementation-specific precedent for something like this in say ghc? Dave Tweed [EMAIL PROTECTED] added: I'd like to second this. It would have been very useful in some of the stuff I've written, particularly since (understandably enough) when using newtype you can't put ! annotations within the data-type. I believe this is what the derive program (available from Glasgow's web site) was originally developed for. -- Alastair Reid Yale Haskell Project Hacker [EMAIL PROTECTED] http://WWW.CS.Yale.EDU/homes/reid-alastair/
[Fwd: Re: Exceptions are too return values!]
--3E1737327A Content-Type: text/plain; charset="us-ascii" Alastair Reid wrote: I believe this is what the derive program (available from Glasgow's web site) was originally developed for. Hi, I'm the implementor of the software formerly known as 'Derive`, and would just make a few points. 1. I've been threatened with legal action from Soft Warehouse Inc. for infringing their trademark "DERIVE". The full details are at http://www.dcs.gla.ac.uk/~nww/Derive/derivehome.html In summary I'm not allowed to refer to my software using that name, and must also inform others to do the same. 2. The software formerly known as 'Derive' was a little project written during the first year of my PhD. Although it originates from Glasgow, one should not assume it enjoys the same level of robustness or support as other Glasgow FP tools. 3. Personally, I think that there is a need for a type-sensitive preprocessor for Haskell. Extending the derivable classes was the motivating example, but there are other applications. The software formerly known as Derive is a first attempt at this. I think it is time for someone to develop this idea properly into a robust system. However, due to my PhD I don't have time for this. regards noel. -- Noel Winstanley Dept of Computing Science University of Glasgow http://www.dcs.gla.ac.uk/~nww/ mailto:[EMAIL PROTECTED] --3E1737327A Content-type: message/rfc822 Return-Path: [EMAIL PROTECTED] Delivery-Date: Mon, 15 Jun 1998 14:58:59 +0100 Received: from dcs.gla.ac.uk by vanuata.dcs.gla.ac.uk id [EMAIL PROTECTED]; Mon, 15 Jun 1998 14:57:05 +0100 Old-Received: from easter.dcs.gla.ac.uk.dcs.gla.ac.uk (actually host easter) by vanuata with SMTP DCS (MMTA) with ESMTP; Mon, 15 Jun 1998 14:56:57 +0100 Old-Received: by easter.dcs.gla.ac.uk.dcs.gla.ac.uk (8.8.5/Dumb)id OAA00405; Mon, 15 Jun 1998 14:56:55 +0100 Old-Received: from haggis.cs.yale.edu (actually host HAGGIS.AI.CS.YALE.EDU) by vanuata with SMTP (MMTA) with ESMTP; Mon, 15 Jun 1998 14:39:41 +0100 Old-Received: from haggis.cs.yale.edu (reid@localhost [127.0.0.1]) by haggis.cs.yale.edu (8.8.7/8.8.7) with ESMTP id JAA27815; Mon, 15 Jun 1998 09:37:53 -0400 Message-Id: [EMAIL PROTECTED] To: Dave Tweed [EMAIL PROTECTED] cc: [EMAIL PROTECTED] Subject: Re: Exceptions are too return values! In-reply-to: Your message of "Mon, 15 Jun 1998 10:03:18 BST." Pine.SGI.3.96.980615095746.2241B-10@neon Sender: [EMAIL PROTECTED] Precedence: bulk 8Qxd$QC/sdeK{93/{KA]T@gir{b8(rd5/zL85UcsTGty!z9Nx%Z+0e193YVEXFcWdM.]+uyVYA6 WNNn]tdh-oQ]/#\R;Vts^}W]a%+%VqSEAu Date: Mon, 15 Jun 1998 09:37:52 -0300 From: Alastair Reid [EMAIL PROTECTED] Resent-Date: Mon, 15 Jun 1998 14:57:05 +0100 Resent-From: [EMAIL PROTECTED] Resent-To: [EMAIL PROTECTED] MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" On Mon, 15 Jun 1998, Fergus Henderson wrote: P.S. Is there any reason why something like `HyperEval' isn't built in to Haskell, or at least include in the Haskell Library report? Is there any implementation-specific precedent for something like this in say ghc? Dave Tweed [EMAIL PROTECTED] added: I'd like to second this. It would have been very useful in some of the stuff I've written, particularly since (understandably enough) when using newtype you can't put ! annotations within the data-type. I believe this is what the derive program (available from Glasgow's web site) was originally developed for. -- Alastair Reid Yale Haskell Project Hacker [EMAIL PROTECTED] http://WWW.CS.Yale.EDU/homes/reid-alastair/ --3E1737327A--
Re: Exceptions are too return values!
Here is an input on the exception handling question: In (pseudo) C++, one can write try { ... } catch (A) { if (C) then handle_it else rethrow } But in a functional language it would be more reasonable to write if (C) then catch(A) { handle_it } or something like that, and let the compiler rewrite it to the C++ construction above (the latter which has the advantage that the handling points are known in advance). Then this could be generalized: If f contains the handling of exceptions E_1, ..., E_k, then f(x) rewrites to catch (E_1, ..., E_k) { f(x), rethrow if not caught } Then f(x) is only computed if needed because it handles the exception, but it also ensures that the exception is handled if f has the capacity to do so. I am not sure how this idea would work out in a functional language, but this would be a part of the analysis one would have to do when implementing exceptions. Hans Aberg * Email: Hans Aberg mailto:[EMAIL PROTECTED] * Home Page: http://www.matematik.su.se/~haberg/ * AMS member listing: http://www.ams.org/cml/
Re: Exceptions are too return values!
At 10:50 +1000 98/06/12, Fergus Henderson wrote: Infinities are probably best treated as a seperate issue. That is, infinities should not correspond to exceptions. If you have a type which supports infinities, then 1/0 should return infinity, not raise an exception. Conversely, if you want 1/0 to not raise an exception, then your type should support infinities. I think it is best to let 1/0 throw an exception "divide-by-zero" -- then this can be used to build types that support infinities (like projective spaces). Hans Aberg * Email: Hans Aberg mailto:[EMAIL PROTECTED] * Home Page: http://www.matematik.su.se/~haberg/ * AMS member listing: http://www.ams.org/cml/
Re: Exceptions are too return values!
At 14:40 1998-06-10 +0100, you wrote: Here's a reasonable design for exceptions in Haskell: * handle :: (String - IO a) - IO a - IO a You probably realized more quickly than I how this can leak exceptions. What I mean is main = do quotient - handle (const (return 0)) (return (0 / 0) -- Looks plausible -- but the exception isn't raised yet. print quotient -- Here the expression 0/0 is evaluated -- and the exception is raised with no handler. Is this considered a drawback? -- Scott Turner [EMAIL PROTECTED] http://www.ma.ultranet.com/~pkturner
Re: Exceptions and return
On 10-Jun-1998, Hans Aberg [EMAIL PROTECTED] wrote: I think that experts on implementing lazy languages can tell you about the problems of implementing C++ "zero-overhead" exceptions (though logically equivalent to an exception monad) into a lazy language like Haskell. There's little point trying to implement C++-like so-called "zero-overhead" exceptions in any language with garbage collection. It would probably result in a performance loss. The "zero-overhead" refers to the time cost of setting up a handler. Because C++ doesn't have garbage collection, any class that does dynamic allocation needs a destructor, and for every local variable with a destructor the implementation needs to set up a handler to call that destructor when an exception is thrown. Since classes with destructors are so common, this needs to be very cheap. C++ implementations are willing to pay a large space overhead, plus a significant increase in implementation complexity, plus an increase in the overhead of actually throwing an exception, in order to reduce the cost of establishing a handler. However, in languages with garbage collection, destructors or finalizers are rare, so there's no need to go to that effort, and even if you did, the increased space costs and resulting decreased locality would probably make it not worthwhile. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions and return
On 10-Jun-1998, Hans Aberg [EMAIL PROTECTED] wrote: At 01:09 +1000 98/06/11, Fergus Henderson wrote: There's little point trying to implement C++-like so-called "zero-overhead" exceptions in any language with garbage collection. It would probably result in a performance loss. This is good indeed. But I think the C++ exceptions have another advantage, namely that it stops further computations and skips directly forward to the first point where the exception is caught. Java, which has garbage collection, probably does the same as in C++, but when using the monadic approach in Haskell it does not work so: The code excepted propagates through a series of identity mappings in the exception monad, which is slow. Well, there's basically three ways that I know of to implement exceptions, other than the C++ "zero-overhead" style: (a) have every function return an indicator saying whether it succeeded or threw an exception, and after each function call this indicator and if it is an exception, rethrow it (b) use a special stack frame for exception handlers; ordinary returns incur no overhead, but throws need to scan the stack looking for handlers (c) use a seperate stack for exception handlers; ordinary returns incur no overhead, and when an exception is thrown it need only look at the topmost entry on the exception handler stack. But having an extra stack complicates things, and may make threads more expensive, etc. Clearly (a) is not very good. You seem to be arguing for (c), but I think that in practice the improvement from (b) to (c) is not likely to be important for the vast majority of programs. I suspect you would be happy with (b). So the real problem is if one can implement an exception monad in Haskell which does not propagate through the code (i.e. skips forward directly to the point where the exception is caught). Yes, this ought to be possible -- indeed SLPJ alluded to doing that kind of thing when he talked about improved I/O performance. I think he was talking about (b) rather than (c), though. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED]| -- the last words of T. S. Garp.
Re: Exceptions are too return values!
On Tue, 9 Jun 1998, Mariano Suarez Alvarez wrote: In a typed language, a function *cannot* be applied to something outside its domain. That's the whole point! That represents a certain degree of idealisation though? E.g., sqrt _as a (single valued) mathematical function_ has domain R^{=0}. Certainly I could define a constructed datatype which is exactly this set. But if I want to use machine floats the natural type is Float-Float. Type classes don't appear to help because a condition for type (i.e., set) membership like (=0) can't in general be decided at compile time. So I seem to be faced with the fact that my argument type is a superset of the domain, and I have to either check at run time or prove that only elements of the domain will ever actually turn up. Types catch lots of errors, but by no means all of them. (Presumably similar examples can occur by, e.g., defining size balanced trees as Tr a = Nd a (Tr a) (Tr a) | Lf which doesn't guarantee that a type-checking expression is necessarily size-balanced.) Or am I missing a terribly obvious point? cheers, dave email: [EMAIL PROTECTED] "Taught the wife some html. __Bad www.cs.bris.ac.uk/~tweed/pi.htm move__." -- Alan Cox work tel: not available
Re: Exceptions and return
Hans Aberg writes: I have noted that C++ exceptions (Exception(a), where a is any piece of data) can be used to implement dynamic versions of C++ constructs such as ``return'' and ``break'' (because I have done it). So exceptions are certainly more general than function returns, at least in this context. Hans Aberg Now, I do not understand that. Certainly one may say that *all* imperative constructs are more general than "ordinary" function returns (trivial Monad...), and in particular with the 'C' longjump you may do horrible things, but if the environment trapping is just used to implement "return", then I believe that all that can be "functionalized" through continuations. With -- quite functional -- call/cc in Scheme (or other mechanisms dealing with first class continuations) you may implement even more exotic control constructs, coroutines, asynchronous message dispatching, etc. They are still functional, but they include the hidden state transformation. I believe that Hans standpoint depends on his vision of the *state* of the system. ** Yours Jerzy Karczmarczuk University of Caen, France
Re: Exceptions and return
At 01:09 +1000 98/06/11, Fergus Henderson wrote: There's little point trying to implement C++-like so-called "zero-overhead" exceptions in any language with garbage collection. It would probably result in a performance loss. This is good indeed. But I think the C++ exceptions have another advantage, namely that it stops further computations and skips directly forward to the first point where the exception is caught. Java, which has garbage collection, probably does the same as in C++, but when using the monadic approach in Haskell it does not work so: The code excepted propagates through a series of identity mappings in the exception monad, which is slow. So the real problem is if one can implement an exception monad in Haskell which does not propagate through the code (i.e. skips forward directly to the point where the exception is caught). Hans Aberg * Email: Hans Aberg mailto:[EMAIL PROTECTED] * Home Page: http://www.matematik.su.se/~haberg/ * AMS member listing: http://www.ams.org/cml/
RE: Exceptions are too return values!
It's nice to have SOME way of handling exceptions, but... The implementation does not keep sets of exceptional values, of course. It simply propagates the first one it trips over to the nearest enclosing handler. One argument that can be made in favour of a generalised more IEEE-like mechanism is that it is usually such a pain to handle an exception that propagates like this that one most often does not bother to handle it properly (i.e. try to continue with the task, which is usually the best thing to do; user hitting 'break' or 'esc' excepted, but those are not 'exceptions' in this sense). And in many cases, using some reasonable 'continuation value' (which have already been specified and widely implemented for f.p. arithmetic) and a set of notes on exceptions that have occurred, is sufficient and gives a nicer behaviour of the program. Say that the application is to produce a simple function curve, for a function given as argument, so only the type is known and no other properties. Say that it does this by computing a list of pairs later to be turned into a nice-looking graph. Say also that overflows occur, or out-of-domain-errors occur. Having these errors propagate up to an IO monad or similar for handling, then having to restart, in the handler, the graph calculation at the appropriate place is much more difficult to handle (and is likely not to be done, or to be done in a buggy way) than just plodding on as if (nearly) nothing out of the ordinary happened. I don't think that this situation is so exotic that one can safely ignore it. Indeed this behaviour has been specified as the default behaviour for IEEE f.p. And it is usually better to let the application continue as normal, as long as no very critical error has occurred. (Note that not even divide-by-zero is considered critical in the IEEE world. It does not even return a NaN, unless the numerator is also zero (or a NaN).) What one would need to do to obtain this, not that I'm suggesting it very strongly, would be to generalise the IEEE model of exceptions from f.p. arithmetic to values in general, including adding Not-a-Proper-Value (NaPV) values to each non-f.p. datatype. A value of Haskell type T can be one of the values we know and love (bottom, or constructor, or function, depending on T), or NaPV (except for f.p. datatypes which already have NaN values) AND, implicitly, it has a set of exception values (this set is bottom if the value part is bottom). Strict operations would propagate exceptions and NaPV values. Certain predefined functions would be allowed to "read", or "replace" the exception set part, something like: add_exceptions :: a - Exceptions - a where the Exceptions would be built into the result, and read_and_clear_exceptions :: a - (a - Exceptions - b) - b where the function argument would be given the value with cleared Exceptions part, and as a second argument, the given Exceptions part. And all is purely functional... (Unless I missed something.) Yes, I have been ignoring performance issues. Generating and keeping Exceptions values around everywhere can be very taxing. It would be helpful to have an easy way of saying that the Exceptions part need not be built-into the value (effectively clearing it, though the proper Exceptions are propagated), changing the underlying datatype and delaying the building-in. It still may not be easy to get this very efficient. Indeed in the imperative (and IEEE arithmetic)world it is never built-in, but one has one exceptions value per thread 'on the side' (limited to arithmetic exceptions). (One such value per process is not sufficient these days when multi-threaded processes are used.) /kent k
Re: Exceptions are too return values!
On Mon, 8 Jun 1998, S. Alexander Jacobson wrote: 1. it is not logically consistent to treat exceptions as return values A function cannot do anything but return a value, can it? For example, suppose that we define a new function: foo' a b = a + b -- foo' is strict in its arguments Our intuition is that foo' is commutative. foo' a b = foo' b a. But that turns out not to be true when you have exceptions. That's the problem with intuitions: they can be wrong... Anyhow, if one is to have exceptions procteting +, I don't think that commutativity of foo' is reasonable: to handle exceptions, you have to do checks, and that you can only do in one order or another. Take x and y from before, z = foo' x' y' What is the value of z? Haskell does not promise to evaluate arguments in any particular order so, depending on implementation, z may be either Exception DivideByZero or Exception NotFactorialDomain -1. Actually, using a monad to manage exceptions you can (maybe, have to) choose a definite order of evaluation of non-exceptionality-conditions. Truly exceptional conditions are those that truly are outside of the domain of the function being evaluated. e.g. factorial -1 The VALUE of (factorial -1) is not an exception. Neither is the value of (factorial (1 `div` 0)). When a function is passed bad arguments, it is not meaningful (from a functional perspective) to have it return a value. In a typed language, a function *cannot* be applied to something outside its domain. That's the whole point! The value of a function over arguments outside its domain is undefined. When such an event occurs, the logically consistent behavior is to exit function evaluation and tell the caller what was wrong with the arguments passed (to the extent it is possible to do). One can rightfully argue that, if one is willing to consider bottom (which is a value we cannot test for!) a return value, which we are, considering an exception a return value is *very* consistent. -- m --- Mariano Suarez Alvarez The introduction of Departamento de Matematica numbers as coordinates Universidad Nacional de Rosario [...] is an act of violence Pellegrini 250 A. Weyl 2000 Rosario - Argentina e-mail: [EMAIL PROTECTED] ---
Re: Exceptions are too return values!
Alex Jacobson: Ooops, I forgot to remove the "and". Anyway, my point is that 1. it is not logically consistent to treat exceptions as return values 2. as an implementation matter it violates laziness to do so OK, now I follow. And diagree. ;-) On your second point first: I'm not sure what you mean by "violates laziness"; it would be true to say that adding exceptional return values in a given way might well reduce the laziness of the program; but this can always be obviated, given sufficient care. Using error-monad syntax might be a bit more palatable, but amounts to essentially the same thing. Alternatively, you can define HOFs to "lift" an n-ary function to an exception-propagating equivalent: That is what I did with my Exception version 2 syntax. The problem is that doing this lifting ends up being non-lazy. You're right, it alters the strictness of the program. But one can recover the original behaviour by delaying/eliminating the pattern-match, though I again I agree this is a pain. You could argue that this problem is an artifact of the Haskell syntax and that we could add Exceptions to the Thunk to achieve the desired result (treating exceptions as return values). I don't think I would, though! (If I understand what you mean by this.) The "problem" is an artifact of wanting to keep the language referentially transparent, which a built-in throw/catch scheme of the sort you suggest would scupper. Our intuition is that foo' is commutative. foo' a b = foo' b a. But that turns out not to be true when you have exceptions. That's true. And it remains true _however_ one treats exceptions. There's no way around that in general, I'm afraid, and I'll cite you assorted papers on Observable Sequentiality if you really want the grubby details. But the real point here is that Exceptions are, by definition, results that are outside the domain of the function being evaluated. Treating exceptions as algebraic types may be comforting, but what you are really doing in that case is extending the domain of your function Effectively, yes. From a domain theory PoV, this is all that one could possibly ever do, in fact ('error' included). -- and there are limits to how far you can go with that. These being? Truly exceptional conditions are those that truly are outside of the domain of the function being evaluated. e.g. factorial -1 The VALUE of (factorial -1) is not an exception. Neither is the value of (factorial (1 `div` 0)). When a function is passed bad arguments, it is not meaningful (from a functional perspective) to have it return a value. The value of a function over arguments outside its domain is undefined. When such an event occurs, the logically consistent behavior is to exit function evaluation and tell the caller what was wrong with the arguments passed (to the extent it is possible to do). I don't find this argument at all compelling. If a value is "truly outside the domain of the function being evaluated", then don't pass it to it! This may seem glib, but I do believe that its better SE practice in general. If there are exceptional conditions in "the world", or if determining a sufficient precondition is not practicable, then I repeat my advice concerning exceptional return values, a necessary evil though they might be. Right now that means using the error function. I am just saying that error isn't really enough for a production quality language. Agreed. Does this make more sense? It makes perfect sense, but I think that having exceptions as a language mechanism in Haskell is not realistic or viable, for the reasons I outlined before. I don't pretend that the alternatives are trivial, or even necessarily very pleasant-looking -- just that they're necessary. Slainte, Alex.