Hi all, For years I have wondered why the Num class has the Eq class and the Show class as super classes. Because of this, I cannot make functions an instance of Num (becuase they are not in Eq or Show). Or a datatype respresenting an infinite amount of digits (because Eq would not make any sense). Now I have found out the reason! However, it does not make me happy, it makes me even more sad. It is of the defaulting mechanism of course! The defaulting mechanism works as follows: If there is an unresolved overloading error on a type variable a, which has as an *only* constraint (Num a), then we take a to be the suitable default. If Show were not a super class of Num, the following program would generate an error: main = print 42 If Eq were not a super class, the following program would not work: main = print (if 42 == 42 then "koe" else "apa") These programs are all fixed by inserting Show and Eq as super classes of Num. So that one does not even notice! Until now. I am interfacing to an external library that uses double-precision floating points internally for all numbers. This is to be as general as possible. However, I know that when I put for example an Integer in, I get one out too. Thus, I want to give a Haskell interface that can deal with this by any numeric type. So I define a type class: class Num a => Number a where convertToDouble :: a -> Double convertFromDouble :: Double -> a (somehow the Haskell numerical hierarchy does not even let me define general functions that do this! -- but that is besides the point.) instance Number Int instance Number Integer instance Number Float instance Number Double ... All my library functions now have the shape: libraryFunction :: Number a => ... a ... Where as actually: primLibraryFunction :: ... Double ... And now the bad thing... When I use "libraryFunction" on a numeric constant, such as 42, I get the error: ERROR "library.hs" (line 8): Unresolved overloading *** Binding : main *** Outstanding context : Number b This is really annoying, and it is not clear why the default mechanism works this way. So here are my questions. Why does the default mechanism have this restriction? I know that the default mechanism is already broken (some desirable properties are destroyed) -- what properties will be broken by lifting this restriction? /Koen. -- Koen Claessen http://www.cs.chalmers.se/~koen phone:+46-31-772 5424 mailto:koen@cs.chalmers.se ----------------------------------------------------- Chalmers University of Technology, Gothenburg, Sweden
Wed, 18 Oct 2000 12:57:56 +0200 (MET DST), Koen Claessen <koen@cs.chalmers.se> pisze:
The defaulting mechanism works as follows: If there is an unresolved overloading error on a type variable a, which has as an *only* constraint (Num a), then we take a to be the suitable default.
This is not what the Haskell 98 Report says. Section 4.3.4: "In situations where an ambiguous type is discovered, an ambiguous type variable is defaultable if at least one of its classes is a numeric class (that is, Num or a subclass of Num) and if all of its classes are defined in the Prelude or a standard library (Figures 6--7 show the numeric classes, and Figure 5 shows the classes defined in the Prelude.)" I see no good reason for Show superclass of Num. Eq makes a little more sense, but could be dropped too. It would be inferred separately when a numeric literal is used in a pattern. I agree that the default mechanism is ugly, and that at least the restriction about classes defined in standard libraries should be removed. Clean has per-class defaults. I don't know how conflicting defaults coming from different class constraints should be solved, or what about multiparameter classes, and whether extending the defaulting mechanism is a good idea at all. But since we don't have anything better... -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
Hi Koen, | If Show were not a super class of Num, the following program | would generate an error: | | main = print 42 | | If Eq were not a super class, the following program would | not work: | | main = print (if 42 == 42 then "koe" else "apa") | | These programs are all fixed by inserting Show and Eq as | super classes of Num. So that one does not even notice! Your claims are incorrect. Both of these examples type check without any errors, and regardless of whether Show and Eq are included as superclasses of Num. It is easy to verify this using "Typing Haskell in Haskell" (http://www.cse.ogi.edu/~mpj/thih); I'll attach the script that I used for this below. Put this in the same directory as all the other .hs files and load it into Hugs. Then edit StdPrel.hs to remove the superclasses of cNum, (replace [cEq, cShow] with []), and it will still work. | For years I have wondered why the Num class has the Eq class | and the Show class as super classes. | | Because of this, I cannot make functions an instance of Num | (because they are not in Eq or Show). Or a datatype | representing an infinite amount of digits (because Eq would | not make any sense). | | Now I have found out the reason! I don't think you have. I do not know the reason either, but I suspect that it is largely historical; when Haskell was first designed, the only types that people wanted to put in Num were also equality and showable types. By making Eq and Show superclasses of Num, types could sometimes be stated more concisely, writing things like (Num a) => ... instead of (Num a, Eq a, Show a) => ... In the past ten years since the Haskell class hierarchy was, more or less, fixed, we've seen several examples of types that don't quite fit (Like functions, computable reals, etc. which might make sense in Num but not in Eq). A natural conclusion is that several of the superclass relations between classes should be removed. But realize that there is an unavoidable compromise here: generality versus the convenience of shorter types. I suggest that there is no point on the spectrum that would keep everybody happy all the time. | It is of the defaulting mechanism of course! | ... Defaulting is a red herring in trying to understand why Show and Eq are superclasses of Num. Marcin has already pointed out that your description of the Haskell defaulting mechanism is not correct by quoting from the Haskell report. You can find another description, again based on the report, in the thih paper. | So I define a type class: | class Num a => Number a where | convertToDouble :: a -> Double | convertFromDouble :: Double -> a |... | All my library functions now have the shape: | libraryFunction :: Number a => ... a ... | ... | And now the bad thing... When I use "libraryFunction" on a | numeric constant, such as 42, I get the error: | | ERROR "library.hs" (line 8): Unresolved overloading | *** Binding : main | *** Outstanding context : Number b | | So here are my questions. Why does the default mechanism | have this restriction? I know that the default mechanism is | already broken (some desirable properties are destroyed) -- | what properties will be broken by lifting this restriction? Defaulting only kicks in if (a) at least one class is numeric, and (b) all classes are standard. Number is not a standard class (you just defined it yourself), so defaulting will not apply. Defaulting was designed to work in this way so that (i) it would catch and deal with the most common problems occurring with numeric literals, and (ii) it would not be used too often; defaulting is in general undesirable because it can silently change the semantics. Again, defaulting is an example of a compromise in the design of Haskell. Ideally, you'd do without it all together, but if you went that way, you'd end up having to write more type information in your programs. And again, I don't suppose there is a universally satisfactory point on this spectrum. All the best, Mark ---------------------------------------------------------------------------- mpj@cse.ogi.edu Pacific Software Research Center, Oregon Graduate Institute Want to do a PhD or PostDoc? Interested in joining PacSoft? Let us know!
participants (3)
-
Koen Claessen -
Mark P Jones -
qrczak@knm.org.pl