opPow, opDollar
Matti Niemenmaa
see_signature at for.real.address
Sun Nov 8 10:48:47 PST 2009
Stewart Gordon wrote:
> Matti Niemenmaa wrote:
> <snip>
>> It's essentially because Haskell has separate type classes (kiiiinda
>> like D interfaces... I won't go into that topic) for integers,
>> fractional numbers, and floating-point numbers. In D the types of
>> those three operators could be something like:
>>
>> anyinteger ^(anyinteger base, anyinteger exponent);
>> anyfractional ^^(anyfractional base, anyinteger exponent);
>> anyfloating **(anyfloating base, anyfloating exponent);
> <snip>
>
> You've merely expanded on what I'd already made out - it doesn't explain
> why these generic functions can't share the same name. Is it because
> Haskell doesn't support function overloading as D does, or for some
> other reason?
The former. Haskell does function overloading via type classes.
I think that the reason why these functions can't have the same name is
that they should all have a single, well-defined type and value. If
they're all called 'pow', what is the type of pow? It can't have all
three types at once, that makes no sense. And what happens if I give pow
to a higher-order function: which one does it end up calling? You'd need
some kind of notation to disambiguate. The developers of Haskell
evidently opted to simply force differently-typed values to have
different names, instead of being able to give them all the same name
but then having to qualify which one you mean whenever you use it.
That'd pretty much amount to them having different names anyway, I think.
Just to show that this quality of Haskell isn't very limiting in
practice, a somewhat tangential explanation of the way these
exponentiation functions are overloaded follows.
The types of these functions in Haskell are (read '::' as 'has type',
the type after the last '->' as the return value and the others as the
parameters):
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: (Floating a) => a -> a -> a
The part before the '=>' is the class context, restricting the type
variables 'a' and 'b'. 'a' and 'b' can be any type at all, as long as
they satisfy the constraints. For instance, for (^), the base can be of
any numeric type, but the exponent must be integral, and the result is
of the same numeric type as the base. So when you're actually using the
function, you might be using it under any of the following types:
(^) :: Integer -> Integer -> Integer
(^) :: Float -> Integer -> Float
(^) :: Double -> Int8 -> Double
As you can see, the functions are already overloaded, in a sense. What
Haskell does not support is 'overloading the implementation' the way
derivatives of C++ (or whatever language first came up with this) do: a
function cannot have different implementations for different types.
Instead, a type class defines certain methods that each type that is an
instance of it must implement. For example, (^) could be defined in
terms of (==), (*), and (-), like so:
base ^ pow = if pow == 0 then 1 else base * (base ^ (pow-1))
(*) and (-) are methods of the Num class, and (==) belongs to a
superclass of Num, so we can infer the type of this as:
(^) :: (Num a, Num b) => a -> b -> a
(The standard-library one restricts b to Integral, because this kind of
definition is obviously valid only for integer exponents.)
We now have a generic implementation of (^) that works for any two
number types. What we can't do is say that it should do something
different for certain types: its definition shows that it depends only
on the methods (==), (*), and (-), so if we want to change the behaviour
of (^) we can do so only by changing their behaviour. This is doable
only by changing the Num instance involved, which can only be done by
changing the types in question.
The only things that can change their behaviour directly depending on
the types involved are class methods, which are defined separately for
each type. For instance, (-) is defined for Integers as bignum
subtraction and (-) for Floats is some kind of built-in operation which
eventually compiles to an fsub on x86. In fact, (**) is a method of the
Floating class, and thus has a separate implementation for all
floating-point types.
--
E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
More information about the Digitalmars-d
mailing list