Num class

Marcin 'Qrczak' Kowalczyk
Thu, 19 Oct 2000 13:11:04 +0200 (CEST)

On Thu, 19 Oct 2000, Tom Pledger wrote:

>       = case filter p (subtypes t) of
>             []   -> t
>             [t'] -> tryToBeMoreSpecific p t'
>             ts   -> case filter p (leastSpecificCommonSubtypes ts) of
>                         [t''] -> tryToBeMoreSpecific p t''
>                         _     -> --ambiguity

Why it is bad:

- Adding a subtype elsewhere may make a program ambiguous. (Well, it is so
  with classes too, but at least it occurs only for overlapping instances,
  not unrelated subtypes of a generic type.)

- Assuming that "more specific" means something like "subtype", types
  are usually put in some ordered sequences. This means that it is not
  enough for each type to know a basic framework, but it must also
  know a type just above or below it. When various types come from
  unrelated sources, it is unlikely that they will know one another
  in the right order.

  For example when we have sized integer types (Int8 etc.) and
  types that are mirrors of C types (CInt, CLong etc.), the sequence
  of subtypes is a mix of both families. Should both families know
  each other? When Posix types (CPid etc.) are added, they are
  again mixed. I can't imagine where all these subtyping instances
  would be defined.

  What is worse, whether CLong is smaller or larger than Int is
  implementation defined. Currently it does not prevent having
  an implementation independent set of instances. Conversion in
  both directions is explicit anyway, and literals just have the
  right type. With your proposal a type that wants to put itself
  at the right place in the sequence containing Int and CLong is
  in trouble.

  Of course some of these types could be defined as synonyms, but
  it's not necessarily a good idea in general. It would make
  correctness of a code dependent on the implementation, by not
  catching code that makes unportable assumptions about type

- When Int and CLong are isomorphic and thus declared subtypes of each
  other, wouldn't your proposal make the compiler loop? It's getting
  hairier and hairier.

> It's more complicated for the implementer, but makes things simpler
> for the programmer because we can then remove the Integral class and
> some unintuitive dualities like length/genericLength.

I doubt it's simpler fot the programmer. Certainly not simpler for me:
I know how the current rules work but I don't know how subtyping could
work :-)

> There should be no subtype relationship between Int8 and Word8 in
> either direction, because they denote different things: numbers and
> encodings respectively.

I hope we are not misunderstood. Word8 in GHC is an integer type
representing values 0..255. It is definitely a number, in the same sense
as Int8. Only their ranges are not contained in one another.

> If Word8 literals are required, as opposed to terms like fromEnum 10,
> they should have some other appearance like 10W.

And you are saying that your proposal is less ugly than the current
state? :-)

> I'd be very cautious about putting Double into any subtype
> relationship at all, because its meaning is tied back into its
> representation.

But people need to use floating point literals!

Each Double is representable as Rational. Your proposal thus lets 0.125
be typed as Double, which can be implicitly coerced to Rational when
needed. What about 0.1? It would lose precision when going to Rational
through Double. OTOH it should definitely be allowed as a Double value
too. How would you allow 0.1 to be used as either Rational or Double?

> If you can't explain that "every X is a Y" without referring to
> representation issues, you shouldn't be declaring X as a subtype of Y!

That's why subtypes are not a right tool for typing numeric literals :-)
(Assuming that they can fit Haskell at all.)

Marcin 'Qrczak' Kowalczyk