Revamping the numeric classes
Tom Pledger
Tom.Pledger@peace.com
Thu, 8 Feb 2001 21:00:58 +1300 (NZDT)
Dylan Thurston writes:
:
| (A question in the above context is whether the literal '0' should
| be interpreted as 'fromInteger (0::Integer)' or as 'zero'.
| Opinions?)
Opinions? Be careful what you wish for. ;-)
In a similar discussion last year, I was making wistful noises about
subtyping, and one of Marcin's questions
http://www.mail-archive.com/haskell-cafe@haskell.org/msg00125.html
was whether the numeric literal 10 should have type Int8 (2's
complement octet) or Word8 (unsigned octet). At the time I couldn't
give a wholly satisfactory answer. Since then I've read the oft-cited
paper "On Understanding Types, Data Abstraction, and Polymorphism"
(Cardelli & Wegner, ACM Computing Surveys, Dec 1985), which suggests a
nice answer: give the numeric literal 10 the range type 10..10, which
is defined implicitly and is a subtype of both -128..127 (Int8) and
0..255 (Word8).
The differences in arithmetic on certain important range types could
be represented by multiple primitive functions (or perhaps foreign
functions, through the FFI):
primAdd :: Integer -> Integer -> Integer -- arbitrary precision
primAdd8s :: Int8 -> Int8 -> Int8 -- overflow at -129, 128
primAdd8u :: Word8 -> Word8 -> Word8 -- overflow at -1, 256
-- etc.
instance Additive Integer where
zero = 0
(+) = primAdd
...with similar instances for the integer subrange types which may
overflow. These other instances would belong outside the standard
Prelude, so that the ambiguity questions don't trouble people (such as
beginners) who don't care about the space and time advantages of fixed
precision integers.
Subtyping offers an alternative approach to handling arithmetic
overflows:
- Use only arbitrary precision arithmetic.
- When calculated result *really* needs to be packed into a fixed
precision format, project it (or treat it down, etc., whatever's
your preferred name), so that overflows are represented as
Nothing.
For references to other uses of class Subtype see:
http://www.mail-archive.com/haskell@haskell.org/msg07303.html
For a reference to some unification-driven rewrites, see:
http://www.mail-archive.com/haskell@haskell.org/msg07327.html
Marcin 'Qrczak' Kowalczyk writes:
:
| Assuming that Ints can be implicitly converted to Doubles, is the
| function
| f :: Int -> Int -> Double -> Double
| f x y z = x + y + z
| ambiguous? Because there are two interpretations:
| f x y z = realToFrac x + realToFrac y + z
| f x y z = realToFrac (x + y) + z
|
| Making this and similar case ambiguous means inserting lots of explicit
| type signatures to disambiguate subexpressions.
|
| Again, arbitrarily choosing one of the alternatives basing on some
| set of weighting rules is dangerous,
I don't think the following disambiguation is too arbitrary:
x + y + z -- as above
--> (x + y) + z -- left-associativity of (+)
--> realToFrac (x + y) + z -- injection (or treating up) done
-- conservatively, i.e. only where needed
Regards,
Tom