Re: Haskell 1.3
It looks ugly, but we could say that a data declaration does not have to have any constructors: data Empty = -- Lennart I agree that the best way to fix this is to have a form of data declaration with no constructors, but I'm not keen on the syntax you propose. How about if we allow the rhs of a data declaration to be just `empty', where `empty' is a keyword? data Empty = empty -- P
Re: Haskell 1.3
Lennart Augustsson wrote: It looks ugly, but we could say that a data declaration does not have to have any constructors: data Empty = Philip Wadler responded: I'm not keen on the syntax you propose. How about if we allow the rhs of a data declaration to be just `empty', where `empty' is a keyword? data Empty = empty Another suggestion is to omit the equal sign, as in data Empty Cheers, Ronny Wichers Schreur [EMAIL PROTECTED]
Re: Haskell 1.3: modules module categories
With present Haskell modules, it seems that `with' automatically comes with `use' and clutters up your namespace. That's why you sometimes need re-naming when importing. Sorry, I missed that one. Manuel pointed out that with/use is already contained in the `qualified names'-proposal. When I'm comparing Haskell to Ada, it seems that basically import Foo = with Foo; use Foo; import qualified Foo= with Foo; Still I'd like to have Ada's `use' on its own, as in with Text_Io; package Foo is ... procedure Bar is use Text_Io; begin ... end; ... end Foo; And while we're at it, what about - nested modules - with possibly private sub-modules similar to the Ada(-95) things. -- Johannes Waldmann, Institut f\"ur Informatik, UHH, Jena, D-07740 Germany, (03641) 630793 [EMAIL PROTECTED] http://www.minet.uni- jena.de/~joe/ ... Im naechsten Heft: Als Arbeiter in einer Radiofabrik - Freundschaft mit dem Sohn eines Luftwaffengenerals - Das KGB ueberwacht den Amerikaner auf Schritt und Tritt - Alarmierende Verdachtsmomente bei der Kaninchenjagd - Ungluecklich verliebt in eine rothaarige Juedin
Re: Haskell 1.3 (newtype)
Sebastian suggests using some syntax other than pattern matching to express the isomorphism involved in a newtype. I can't see any advantage in this. Further, Simon PJ claims that if someone has written data Age = Age Int foo (Age n) = (n, Age (n+1)) that we want to be able to make a one-line change newtype Age = Age Int leaving all else the same: in particular, no need to add twiddles, and no changes of the sort Sebastian suggests. I strongly support this! (No, Simon and I are not in collusion; indeed, we hardly ever talk to each other! :-) Cheers, -- P
Re: Haskell 1.3 (newtype)
Well, I'm glad to see I provoked some discussion! Simon writes: Lennart writes: | So if we had | |data Age = Age !Int |foo (Age n) = (n, Age (n+1)) | | it would translate to | |foo (MakeAge n) = (n, seq MakeAge (n+1)) | | [makeAge is the "real" constructor of Age] Indeed, the (seq MakeAge (n+1) isn't eval'd till the second component of the pair is. But my point was rather that foo evaluates its argument (MakeAge n), and hence n, as part of its pattern matching. Hence foo is strict in n. Why should foo evaluate its argument? It sounds to me like Lennart is right, and I should not have let Simon lead me astray! I think its vital that users know how to declare a new isomorphic datatype; it is not vital that they understand strictness declarations. Hence, I favor that newtype Age = Age Int data Age = Age !Int be synonyms, but that both syntaxes exist. This is assuming I have understood Lennart correctly, and that foo (Age n) = (n, Age (n+1)) foo' a = (n, Age (n+1)) where (Age n) = a are equivalent when Age is declared as a strict datatype. Unlike Sebastian or Simon, I believe it would be a disaster if for a newtype one had to distinguish these two definitions. Cheers, -- P
Re: Haskell 1.3 (newtype)
Lennart writes: | So if we had | | data Age = Age !Int | foo (Age n) = (n, Age (n+1)) | | it would translate to | | foo (MakeAge n) = (n, seq MakeAge (n+1)) | | [makeAge is the "real" constructor of Age] | | Now, surely, seq does not evaluate its first argument when the | closure is built, does it? Not until we evaluate the second component | of the pair is n evaluated. Indeed, the (seq MakeAge (n+1) isn't eval'd till the second component of the pair is. But my point was rather that foo evaluates its argument (MakeAge n), and hence n, as part of its pattern matching. Hence foo is strict in n. Sebastian writes: | Is it really a good idea to extend the language simply to allow foo and | foo' to be equivalent? The effect of foo' can still be achieved if Age is | a strict data constructor: | | data Age = Age !Int | | foo'' :: Age - (Int, Age) | foo'' a = (n, Age (n+1)) where (Age n) = a | | and compilers are free (obliged?) to represent a value of type Age by an | Int. Indeed, it's true that foo'' does just the right thing. Furthermore, I believe it's true that given the decl data T = MkT !S the compiler is free to represent a value of type T by one of type S (no constructor etc). Here are the only real objections I can think of to doing "newtype" via a strict constructor. None are fatal, but they do have a cumulative effect. 1. It requires some explanation... it sure seems a funny way to declare an ADT! 2. The programmer would have to use let/where bindings to project values from the new type to the old, rather than using pattern matching. Perhaps not a big deal. 3. We would *absolutely require* to make (-) an instance of Data. It's essential to be able to get data T = MkT !(Int - Int) 4. We would only be able to make a completely polymorphic "newtype" if we added a quite-spurious Data constraint, thus: data Data a = T a = MkT !a (The Data is spurious because a value of type (T a) is going to be represented by a value of type "a", and no seqs are actually going to be done.) 5. We would not be able to make a newtype at higher order: data T k = MkT !(k Int) because there's no way in the language to say that (k t) must be in class Data for all t. [This is a somewhat subtle restriction on where you can put strictness annotations, incidentally, unless I've misunderstood something.] Simon
Re: Haskell 1.3 (Bounded;fromEnum;type class synonyms)
Dear Sverker Nilsson, Thanks for your message - interesting ideas and interesting questions. [I'm copying the reply to the Haskell mailing list in case anyone wishes to support your suggestions.] First, one of Haskell's annoying features is that the scope of a type variable in a type signature or instance heading only extends over the signature. So, when you want to write: instance (FromInt a, ToInt a, MinVal a, MaxVal a) = Enum a where enumFrom c = map fromInt [toInt c .. toInt (maxVal :: a)] It doesn't work (because the "a" isn't in scope during the declarations) - you have to use "asTypeOf" instead: instance (FromInt a, ToInt a, MinVal a, MaxVal a) = Enum a where enumFrom c = map fromInt [toInt c .. toInt (maxVal `asTypeOf` c)] While developing something like the proposed "Bounded" class, you introduced separate classes for minVal and maxVal observing: Something having a minimum value, in my view, didn't necessarily imply it would have a maximum value. Yes, perfectly true. The best example is that there's a minimal list (the empty list) but even though there's a maximal Char (say), there's no maximal list of characters. Our primary motivation for adding Bounded is to clean up the {min,max}{Char,Int} situation and make the derived Enum instances slightly more regular (similar in spirit to your definitions above). For this purpose, insisting on having both a min and a max isn't a problem. However, for other purposes, having one bound but not the other is certainly possible and maybe useful. (I agree that defining a bogus instance in which "minVal" (say) is defined but "maxVal" is undefined or has a bogus value is at least untidy and at worst a bug waiting to happen. I tried (and failed) to get the Text instance of (a - b) removed from the Prelude for this reason.) The major disadvantage of separating the two is that it introduces even more classes. If you read the preludechanges document carefully, you'll see that (even at this late stage) these are only proposed changes. Glasgow argue that it's hard enough to keep Ix and Enum separate in your mind - adding another can only worsen things. You were then surprised and disturbed to find that this isn't legal Haskell: class (MinVal a, MaxVal a)=Bounded a instance Bounded T where maxVal = T3 minVal = T1 There was a proposal to make this legal. As far as I know, there's no technical problems here - I guess it just got forgotten about (or the proposer decided that Haskell 1.3 had too many changes in it already!) * Should Bounded be derived from Ord? The Bounded class that was suggested for Haskell 1.3 was derived from Ord. Myself playing with similar things I derived MinVal and MaxVal from nothing - I thought this more general. Maybe the reason for having Bounded derived from Ord was to imply that its functions shall satisfy certain laws, probably as being min/max as defined by the ordering functions in Ord. But as I don't see how this can be guaranteed by deriving Bounded from Ord, I would think that it could as well be standalone (or derived from something like MinBound and MaxBound if possible); for more generality and less dependency between the classes in the system. Yes, the sole reason is because it seemed tidier to specify Ord - without knowing which comparision is being used, it doesn't make much sense to say you have a "maximum value". For example, the new proposal says: ... Programmers are free to define a class for partial orderings; here, we simply state that Ord is reserved for total orderings. That seems to imply also that a programmer should not use Bounded on types that have no total ordering. I believe this might be an unnecessary restriction. It certainly looks that way. The names fromEnum and toEnum are misleading since their types involve both Enum and Bounded. We couldn't face writing fromBoundedEnum and toBoundedEnum. Suggestions welcome. Maybe names like ToInt and FromInt could be used for this? How about the following, assuming the proposed diff and succ functions: class (Bounded a, Enum a) = ToInt a where toInt :: a - Int [...] class (Bounded a, Enum a) = FromInt a where fromInt :: Int - a [...] These names look good. Three _minor_ concerns: 1) It introduces even more standard classes to confuse programmers with. Why allow the programmer to override them? 2) Several implementations have added a non-standard method fromInt :: Int - a to the Num class to avoid unnecessary uses of fromInteger. However, I think most normal uses would work unchanged if "fromInt" had type: fromInt :: (Bounded a, Enum a) = Int - a 3) There is a weak tradition of putting the name of the class into the name of the method. This tradition is often broken when it would get in the way of a good name. Action: 1) I'll remove the Ord context from Bounded. 2) I'll use fromInt/toInt names instead
Re: Haskell 1.3 (newtype)
On Tue, 12 Sep 1995, Lennart Augustsson wrote: The posted semantics for strict constructors, illustrated by this example from the Haskell 1.3 post, is to insert seq. data R = R !Int !Int R x y = seq x (seq y (makeR x y)) -- just to show the semantics of R So if we had data Age = Age !Int foo (Age n) = (n, Age (n+1)) it would translate to foo (MakeAge n) = (n, seq MakeAge (n+1)) [makeAge is the "real" constructor of Age] I had assumed (as Simon seems to) that the semantics of pattern matching against a strict constructor would accord with the following: 1. matching a simple pattern involves evaluating the expression being matched to the point that its outermost constructor is known 2. for strict constructors this must result in the annotated constructor argument(s) being evaluated From what Lennart says, this is not the intended semantics. So what *is* the intended semantics? Sebastian Hunt
Re: Haskell 1.3 (lifted vs unlifted)
John Hughes mentioned a deficiency of Haskell: OK, so it's not the exponential of a CCC --- but Haskell's tuples aren't the product either, and I note the proposal to change that has fallen by the wayside. and Phil Wadler urged to either lift BOTH products and functions, or none of them. My two pence: If functions/products are not products and exponentials of a CCC, you should aim for the next best thing: an MCC, a monoidal closed category. But Haskell's product isn't even monoidal: There is no type I such that A*I and A are isomorphic. The obvious candidate (in a lazy language) would be the empty type 0, but A*0 is not isomorphic to A but to the lifting of A. Another problem: the function space A*B - C should be naturally isomorphic to A - (B - C). What does the iso look like? One half is the obvious curry function: curry f x y = f(x,y) But what is the other half? Apparently, it should be either uncurry1 f (x,y) = f x y or uncurry2 f (~(x,y)) = f x y Which one is right depends on which one establishes the isomorphism. Consider the definition f1 (x,y) = () Now: uncurry1 (curry f1) undef = undef = f1 undef while on the other hand: uncurry2 (curry f1) undef = curry f1 (p1 undef, p2 undef) = f1(p1 undef,p2 undef) = () =/= f1 undef This suggests that uncurry2 is wrong and uncurry1 is right, but for f2 (~(x,y)) = () the picture is just the other way around. BTW It doesn't help to employ "seq" in the body of curry. Looks rather messy. Can some of this be salvaged somehow? -- Stefan Kahrs
Re: Haskell 1.3 (Bounded;fromEnum;type class synonyms)
* Playing around, learning the basics, reinventing the wheel... I had been playing around with some classes, primarily to learn for myself, being new to the Haskell language, when I got the report on the current status of Haskell 1.3. The classes I had played with had some similarities to some of the proposals for the new prelude, yet I had made it in a quite different way. Trying to combine the two styles, I ran into an unexpected problem. This problem I am naive enough to believe could be solved by a simple language extension. Using Gofer, I had made some classes that could be used for implementing ordering and other things for enumeration (data T=T1 | T2 | T3) types but not restricted to those. I made 4 minimal classes with just 1 function in each. (I thought this would be most general. Something having a minimum value, in my view, didn't necessarily imply it would have a maximum value.) So: class FromInt a where fromInt:: Int-a class ToInt a where toInt:: a-Int class MaxVal a where maxVal:: a class MinVal a where minVal:: a -- I then used this as follows: data T = T1 | T2 | T3 instance ToInt T where toInt e = case e of T1 - 1 T2 - 2 T3 - 3 instance Eq T where a == b = toInt a == toInt b instance Ord T where a = b = toInt a = toInt b -- And so on. The MaxVal and MinVal classes also where used to make a generic -- implementation of a bounded Enum class, generalizing how it was made in the -- Gofer prelude for Char: instance (FromInt a, ToInt a, MinVal a, MaxVal a) = Enum a where enumFrom c = map fromInt [toInt c .. toInt (maxVal `asTypeOf` c)] enumFromThen c c' = map fromInt [toInt c, toInt c' .. toInt (lastVal `asTypeOf` c)] where lastVal = if c' c then minVal else maxVal -- This worked to my great delight! And I had began to learn the basics -- of the type system in Haskell. My only problem was that I had to use -- (maxVal `asTypeOf` c) instead of (maxVal::a). I believe the reason -- for this might be clear when I learn more. Somebody have a clue? * Running into a problem: type class synonyms are not synonymous? Then, I got the report on the developments of Haskell 1.3 and began to read it with great curiosity. I then found the Bounded class, containing corresponding functions to MinVal and MaxVal. A question then occured to me: Why not have separate classes as I had done? Would not that perhaps be more general, increasing the possibilities for reuse? (Without having to stub out one of minBound or maxBound if you use it for a type without one of them.) On the other hand, I saw the convenience of having both minBound and maxBound in the same class, decreasing the number of classes that have to be mentioned in various cases. But I thought, then, why not derive the Bounded class from MinVal and MaxVal - would not that then be equivalent? So I tried class (MinVal a, MaxVal a)=Bounded a -- This was allowed, but then... instance Bounded T where maxVal = T3 minVal = T1 -- That didn't work! (Gofer said: ERROR "tst.gs" (line 45): No member "maxVal" in class "Bounded") Maybe I had done something wrong, or Gofer does not allow something that would be allowed in Haskell? I suspect however that I am simply not supposed to do this in either Haskell or Gofer... Instead I had to use two separate instantiaions, exactly as before I declared the Bounded class: instance MinVal T where minVal = T1 instance MaxVal T where maxVal = T3 This seems to be somewhat unnecessary, wouldn't it be quite possible for a compiler to transform the instantiation of Bounded to the two instantiations of MinVal and MaxVal? Maybe this would be a useful development of Haskell? * Should Bounded be derived from Ord? The Bounded class that was suggested for Haskell 1.3 was derived from Ord. Myself playing with similar things I derived MinVal and MaxVal from nothing - I thought this more general. Maybe the reason for having Bounded derived from Ord was to imply that its functions shall satisfy certain laws, probably as being min/max as defined by the ordering functions in Ord. But as I don't see how this can be guaranteed by deriving Bounded from Ord, I would think that it could as well be standalone (or derived from something like MinBound and MaxBound if possible); for more generality and less dependency between the classes in the system. For example, the new proposal says: ... Programmers are free to define a class for partial orderings; here, we simply state that Ord is reserved for total orderings. That seems to imply also that a programmer should not use Bounded on types that have no total ordering. I believe this might be an unnecessary restriction. * Can toInt be fromEnum and toEnum fromInt? New functions fromEnum and toEnum were proposed to be added to Haskell
Re: Haskell 1.3 (newtype)
The design of newtype appears to me incorrect. The WWW page says that declaring newtype Foo = Foo Int is distinct from declaring data Foo = Foo !Int (where ! is a strictness annotation) because the former gives case Foo undefined of Foo _ - True = True and the latter gives case Foo undefined of Foo _ - True = undefined. Now, on the face of it, the former behaviour may seem preferable. But trying to write a denotational semantics is a good way to get at the heart of the matter, and the only way I can see to give a denotational semantics to the former is to make `newtype' define a LIFTED type, and then to use irrefutable pattern matching. This seems positively weird, because the whole point of `newtype' is that it should be the SAME as the underlying type. By the way, with `newtype', what is the intended meaning of case undefined of Foo _ - True ? I cannot tell from the summary on the WWW page. Defining `newtype' in terms of `datatype' and strictness avoids any ambiguity here. Make newtype equivalent to a datatype with one strict constructor. Smaller language, more equivalences, simpler semantics, simpler implementation. An all around win! Cheers, -- P
Re: Haskell 1.3
I would like to respond to John's note. My response is largely positive, though I disagree with a couple of points. However, it is an independent question whether or not strictness annotations should be applicable to function types. And this is where I disagree with the committee. To quote `Introducing Haskell 1.3', Every data type, except -, is a member of the Data class. In other words, in Haskell 1.3 FUNCTIONS ARE NOT FIRST-CLASS CITIZENS I cannot agree here. Functions are not members of the equality class either, but that does not demote them to second class citizens. However, John may be right in suggesting that people will become more reluctant to use functions as values if they cannot force their evaluation. I see a very great cost in such a philosophical change, and I do not see that the arguments against strictly evaluating function values are so very compelling. Implementation difficulties? hbc has provided it for years, and even under the STG machine is the problem so very much harder than handling shared partial applications correctly? I haven't checked hbc, but I would be interested if someone would confirm that function strictify works properly. It didn't use to in LML. Semantic difficulties? The semantics of lifted function spaces are perfectly well defined. OK, so it's not the exponential of a CCC --- but Haskell's tuples aren't the product either, and I note the proposal to change that has fallen by the wayside. This is probably an important point. I see there being value in two sorts of functions: lifted and non-lifted (or equivalently boxed and unboxed). A lifted function may be expressed as a computation which delivers a function, just like lifted integers are computations which deliver integers. Under this view it would be entirely in keeping with the rest of Haskell for the standard functions to be lifted, and to leave open the possibility in the future of introducing unlifted functions. So here's my proposal: change `Introducing Haskell 1.3' to read Every data type, including -, is a member of the Data class. I am inclined to agree. Is there a problem then that every type is in Data? Not at all. The Data class indicates that forcing has been used in the body of an expression. This is valuable information that is exposed in the type. John.
Re: Haskell 1.3
Let me make one more attempt to persuade the committee to change the way strictness annotations are to be introduced. First of all, let's recognise that strictness annotations and the seq function are of enormous importance; this is a vital extension to the language, not a small detail. Space debugging consists to quite a large extent of placing applications of seq correctly, and we all know what dramatic effects space debugging has been able to achieve. The strictness features are going to be very heavily used in the future. Recording uses of polymorphic strictness annotations using class Data has both advantages and disadvantages. A big disadvantage is that curing a space bug may change the types of many functions in many modules, which at the least may require a lot of recompilation. The programmer who likes to state the type of each function will be especially hard hit, of course, which will unfortunately discourage such a style. But class Data seems to be vital for cheap deforestation, which is such an important optimisation as to outweigh the disadvantages. However, it is an independent question whether or not strictness annotations should be applicable to function types. And this is where I disagree with the committee. To quote `Introducing Haskell 1.3', Every data type, except -, is a member of the Data class. In other words, in Haskell 1.3 FUNCTIONS ARE NOT FIRST-CLASS CITIZENS To design a functional language today, in which this is true, is in my view deeply mistaken. In the past, I've argued that it will be very frustrating for those programmers who do discover they need to apply seq to a function in order to cure a space bug, to find that they are unable to do so. Even more seriously, programmers weighing up a choice of representation for an abstract datatype, choosing between a representation as a function or as a `Data' type, will know that if they choose the function then problems with space debugging may lurk in the future. Excluding (-) from class Data is a step away from true `functional' programming towards a style in which higher-order functions are just a kind of macro. I see a very great cost in such a philosophical change, and I do not see that the arguments against strictly evaluating function values are so very compelling. Implementation difficulties? hbc has provided it for years, and even under the STG machine is the problem so very much harder than handling shared partial applications correctly? Semantic difficulties? The semantics of lifted function spaces are perfectly well defined. OK, so it's not the exponential of a CCC --- but Haskell's tuples aren't the product either, and I note the proposal to change that has fallen by the wayside. Weaker strictness analysis? I'd like to hear the effect quantified. How much slower will Haskell 1.3 run if function spaces are lifted in the semantics? Will it be measurable? I'm prepared to pay a few percent. So here's my proposal: change `Introducing Haskell 1.3' to read Every data type, including -, is a member of the Data class. John Hughes
Re: Haskell 1.3 Draft Report
Hi. For the TeX-impaired, is there any chance of sticking postscript files on an ftp site? Thanks! -- Dave A draft of the Haskell 1.3 report is available by FTP from ftp.dcs.glasgow.ac.uk [130.209.240.50] in pub/haskell/report/draft-report-1.3.dvi.gz [Report] pub/haskell/report/draft-libraries-1.3.dvi.gz [Libraries] Highlights include: Monadic I/O A split into prelude and libraries, with qualified names Strict data types Some minor syntactic revisions We are planning to revise this and release it in time for FPCA '95. There will definitely be additional prelude and library changes; including several new libraries. Feedback is welcome and will be taken into account when revising the report, but please remember that we will be very busy over the next few weeks (I am also away for the next two weeks!). Please mail typos., minor notes on syntax etc. to me; substantive comments should be sent to [EMAIL PROTECTED] Regards, Kevin -- Dave Bakin How much work would a work flow flow if a #include 510-922-5678work flow could flow work? std/disclaimer.h
Re: Haskell 1.3
Ian Holyer writes: To go back to the debate on instances, here is a concrete proposal for handling instances in Haskell 1.3: I can see what you're doing, but I dislike the idea of no longer being able to define instances local to a module. This limits my choice of class and type names, and may cause problems when importing libraries defined by other users. For global (exported) instances your rules make sense (a variant of these was considered at one point) with the caveats marked below. 1) A C-T instance can be defined in any module in which C and T are in scope. Fine, in conjunction with 5 and 2 or similar constraints. 2) A C-T instance defined in module M is in scope in every module which imports from M, directly or indirectly. (If C or T are not in scope, a module just passes the instance on in its interface). You need to ignore local C-T instances (i.e. those where a class C or type T is defined locally and not exported), otherwise mayhem could result. Local instances will now also cause problems if there is a global C-T instance defined in any importing module. The interface is problematic if a new class with local name C or type with local type T is defined (or both!), especially if there is a (local) C-T instance. Getting round this would involve being much more explicit about global names in interface files (e.g. an M1.C-M2.T instance). There is also potential name capture of type, class, or operator names by the importing module, which would require additional checking of interfaces import (something we would like to avoid for efficiency reasons). 3) A C-T instance may be imported more than once via different routes, provided that the module of origin is the same. This implies annotating instances with their module of origin, as you note below. 4) If an application of an overloaded function is resolved locally, the relevant instance must be in scope. ...a relevant instance must be in scope... ^ 5) There must be at most one C-T instance defined in the collection of modules which make up any one program (global resolution occurs in Main). There should be at most one global C-T instance defined (otherwise you lose the ability to create local types with instances)... You also shouldn't specify where resolution takes place. Link resolution is much faster... I would like to see the origin of instances in interface files. My preference from an implementers point of view would be something like: interface M1 whereinterface M3 where import M2 (C(..))or import M2 (C(..)) import M3 (T(..),fT) type T = ... instance C T where f = fT instance C T where f = fT The name fT is invented while compiling M3 and passed around in interface files, but not exported from them into implementation modules. As well as specifying the origin of the instance, it gives the code generator something to link to. This really isn't a problem for an implementation. We can always link to a hidden name derived from the unique C-T combination. Introducing magic names in an interface sounds like a *very bad* idea -- you might well accidentally capture a user- or Prelude-defined name. For example, class From where from :: Int - [a] - a instance From Int where from = ... introduces fromInt in the interface, which will clash with the Prelude name. interface M1 where import M2(C(...)) import M3(T(...)) import M4(instance M2.C M3.T) is probably closer to what's required. Regards, Kevin
Re: Haskell 1.3 [instances]
Ian Holyer writes: The current restriction that instances must be defined either in the class module or the type module is painful. LISTEN TO THIS MAN! Trying to use the module system in (what we imagined to be) a sensible way on the Glasgow Haskell compiler [which is written in Haskell] has been a nightmare. Take a pile of mutually-dependent modules, add the "instance virus" [instances go with the class or type, and you can't stop them...], and you have semi-chaos. All attempts to have export/import lists that "show what's going on" have been undermined by having to add piles of cruft to keep instancery happy. I would go for either of the following not-thought-through choices: * Instances travel with the *type*, not the class. 99% of the time, this is what we want. If your instance isn't going, add an explicit export of the type constructor. Possibly have a special case for instances of user-defined classes for Prelude types... * Make it so that imported instances whose class/type is out-of-scope may be silently ignored (i.e., an exception to the closure rule). For example, if I write "import Foo" and Foo's interface includes "instance Wibble Wobble" and none of my "imports" happen to bring "Wibble" (or "Wobble") into scope, then a compiler may drop this instance silently. It is not an error. (Of course, if you try to *use* such an instance, you will get an error downstream.) Of course, something that involves new syntax/extra machinery would also be fine. Will PS: Get rid of "default" declarations, too. No-one uses them. (Hi, Kevin!)
Re: Haskell 1.3 (n+k patterns)
jl writes: I feel the need to be inflamatory: I believe n+k should go. Again, I agree completely. Let's get rid of this horrible wart once and for all. It's a special case that makes the language more difficult to explain and implement. I've hardly seen any programs using it so I don't think backwards compat is a problem. Anyone who thinks this change will cause them more than 10 minutes work, plese speak up. -- Lennart