[Haskell] Per-type function namespaces (was: Data.Set whishes)
ozone at algorithm.com.au
ozone at algorithm.com.au
Fri Feb 27 15:16:11 EST 2004
On 27/02/2004, at 1:13 PM, oleg at pobox.com wrote:
>> For example, say I'm writing the Data.Complex module; there's a
>> function in that module "phase :: RealFloat a => Complex a -> a". So,
>> how do you put this phase function into a type class? Perhaps you
>> could abstract away from the RealFloat and Complex bits, so you have a
>> phase function which is generalised to work over a Num and an
>> arbitrary data type instead; e.g. "class Phase c where phase :: Num a
>> => c a -> a". But what happens if, say, somebody adds a Moon data
>> type, and they want to write a phase function which returns the phase
>> of such a moon? Phases of the moon certainly aren't Nums, nevermind
>> the fact that you probably want to supply your moon phase's function
>> with some sort of date as an extra parameter, which means the Phase
>> type class isn't flexible enough.
>
> Here's the code that does exactly as you wish:
>
>> {-# OPTIONS -fglasgow-exts #-}
>>
>> import qualified Complex
>>
>> class Phase a b | a -> b where
>> phase:: a -> b
>>
>>
>> instance (RealFloat a) => Phase (Complex.Complex a) a where
>> phase = Complex.phase
>>
>> data MoonPhase = P1 | P2 | P3 | P4 deriving Show
>>
>> instance Phase Int MoonPhase where
>> phase x = if x `mod` 4 == 0 then P1 else P4
>>
>> instance Phase MoonPhase (Int->Int) where
>> phase P1 x = x
>> phase P2 x = x+1
>>
>> main = do
>> putStrLn $ show $ phase ( (1.0::Float) Complex.:+
>> (1.0::Float))
>> putStrLn $ show $ phase (0::Int)
>> putStrLn $ show $ phase P1 (2::Int)
Very, very nice Oleg :). I'm glad to know that we can achieve such
things using the existing type class mechanisms already. However, this
still doesn't solve the problem, because:
1) now I have to manually declare a class definition for every single
function, and I have to declare it in advance before any module defines
that function (most serious problem; see below),
2) I then have to declare instances of that type class for every
function I define,
3) the type signature for phase reveals no clues about how to use that
function.
So unfortunately, this is hardly a scalable solution. The entire
reason I came up with the idea is because if we use type classes to
implement this sort of overloading, we have to know every single
possible function that any module author will ever create, and declare
classes for those functions in advance. This is fine if you're
declaring truly polymorphic functions which are designed from the start
to be totally general, but it is not designed for functions which may
do vastly different things and may contain totally different type
signatures, but share the same name because that would be a sensible
thing to do. (e.g. the phase function mentioned above.)
With the per-type namespace separation I'm advocating, you do not need
to know and declare in advance that each function "will be" overloaded,
you simply write a FiniteMap.add function and a Set.add function, and
you get a simpler form of namespace separation (or overloading) based
on the first parameter that's passed to it. It is a solution which is
more _flexible_ than requiring type class definitions, and it is better
than having hungarian notation for functions. In fact, I think that,
right now, if we replaced the current namespace separation offered by
the hierarchical module system, and instead only had this sort of
per-type namespace separation, things would still be better!
I realise my idea isn't very general in that it only allows this
namespace lookup/overloading based on the type of a single argument
parameter, and I think it would be possible with a bit more thinking to
generalise it to work based on multiple arguments (e.g. via
argument-dependent lookup, or whatnot). But even in its current form,
I honestly think it offers far more flexibility and would lead to
cleaner APIs than is currently possible.
--
% Andre Pang : trust.in.love.to.save
More information about the Haskell
mailing list