Num class

Mark P Jones mpj@cse.ogi.edu
Wed, 18 Oct 2000 14:46:24 -0700


This is a multi-part message in MIME format.

------=_NextPart_000_0001_01C03912.2FCECB10
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

Hi Koen,

| If Show were not a super class of Num, the following program
| would generate an error:
|=20
|   main =3D print 42
|=20
| If Eq were not a super class, the following program would
| not work:
|=20
|   main =3D print (if 42 =3D=3D 42 then "koe" else "apa")
|=20
| 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.
|=20
| 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).
|=20
| 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) =3D> ... instead
of (Num a, Eq a, Show a) =3D> ...

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 =3D> Number a where
|     convertToDouble   :: a -> Double
|     convertFromDouble :: Double -> a
|...=20
| All my library functions now have the shape:
|   libraryFunction :: Number a =3D> ... a ...
| ...
| And now the bad thing... When I use "libraryFunction" on a
| numeric constant, such as 42, I get the error:
|=20
|   ERROR "library.hs" (line 8): Unresolved overloading
|   *** Binding             : main
|   *** Outstanding context : Number b
|=20
| 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!

------=_NextPart_000_0001_01C03912.2FCECB10
Content-Type: text/plain;
	name="SourceFortyTwo.hs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="SourceFortyTwo.hs"

module SourceFortyTwo where

import Testbed
import HaskellPrims
import HaskellPrelude

-------------------------------------------------------------------------=
----
-- Test Framework:

main     :: IO ()
main      =3D test imports fortyTwo

saveList :: IO ()
saveList  =3D save "FortyTwo" imports fortyTwo

imports  :: [Assump]
imports   =3D defnsHaskellPrims ++ defnsHaskellPrelude

-------------------------------------------------------------------------=
----
-- Test Program:

fortyTwo :: [BindGroup]
fortyTwo
 =3D map toBg
   [[("main", Nothing, [([], ap [evar "print", elit (LitInt 42)])])],
    [("main'", Nothing,
     [([], ap [evar "print",=20
               eif (ap [econst eqMfun, elit (LitInt 42), elit (LitInt =
42)])
                   (elit (LitStr "koe"))
                   (elit (LitStr "apa"))])])]]

-------------------------------------------------------------------------=
----

------=_NextPart_000_0001_01C03912.2FCECB10--