[Haskell] Per-type function namespaces (was: Data.Set whishes)

oleg at pobox.com oleg at pobox.com
Sat Feb 28 01:12:21 EST 2004

Simon Peyton-Jones wrote:

> In Haskell today, you can at least tell what value is bound to each
> identifier in the program, *without* first doing type checking. 

I'm afraid I'm confused. In the following code

> data Z
> data S a
> class Card c where c2int:: c -> Int
> instance Card Z where c2int _ = 0
> instance (Card c) => Card (S c) where c2int _ = 1 + c2int (undefined::c)
> foo = c2int (undefined::(S (S (S (S Z)))))

how can one tell the value of foo without first doing the
typechecking? Without typechecking, we can't use c2int and can't even
construct any meaningful value of class Card. For c2int, the type is
the ``value.'' Overlapping instances, polymorphic recursion -- all
seem to make the value determination even more uncertain.

Andre Pang wrote:

> 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),

> However, declaring the instance first requires declaring the type
> class itself, and that _is_ a problem, because that's exactly what I'm
> trying to work around.  Without 20/20 hindsight, you cannot say with
> certainty what type signatures a "generic" function (like 'phase' or
> even 'add') can support, because it's not a generic function,

But in the solution posted previously, for each ad hoc overloadable
function, the corresponding class *always* has the same

	class HasAdd a b | a->b where add:: a->b

Therefore, we don't need clairvoyance to define an overloadable
function that way. If I need an overloadable function add, I can go
ahead and define the above class, and then add an instance. I can do
that without knowing all possible overloadable instances of add,
present or future. What if somebody else did the same in some other
module? If that somebody else followed the conventions, he would
introduce exactly the same class declaration. If GHC or its developers
could somehow be persuaded to overlook _exact_ duplicate class
declarations, then that part of the problem can be solved. The class
declaration itself could perhaps be generated by Template Haskell.

The discussed solution is quite related to some of the Records
proposals (which have been discussed here half a year ago). There too
we have the inconvenience of choosing unique names for field labels.

> data Person = Person {_pname:: String, _paddress:: String}
> data Computer = Computer {_cname::[String], _caddress:: Int}
> class HasName a b | a->b where name:: a->b
> class HasAddress a b | a->b where address:: a->b
> instance HasName Person String where name = _pname
> instance HasAddress Person String where address = _paddress
> instance HasName Computer [String] where name = _cname
> instance (HasName n r) => HasName (Maybe n) (Maybe r) where
>     name = fmap name
> -- Alas, the following will break the dependency...
> --instance (Num a) => HasName a String where name = show
> -- But the following works: overlapping instances at work
> instance (HasAddress a b) => HasName a b where name = address
> instance HasAddress Int (Int->String) where address x y = show (x+y)
> newtype W a = W a   
> instance (Num a) => HasAddress (W a) String where address (W a) = show a
> test2 = let p = Person  "Anonymous"  "N/A"
>             c = Computer ["FQDN","localhost"] 10
> 	in "person named " ++ (name p) ++ " at a computer " ++ (head (name c))
> 	   ++ " and another " ++ (show$ name p1)
>    where p1 = (Nothing::Maybe Person)

The first few lines of the code is boilerplate and could be
automatically generated. As you can see, we can even handle a limited
form of polymorphism, and even do a "hand off". For example, if some
thing doesn't have a name but has an address, we can use the address
as its name. Alas this ``backtracking'' isn't as general as we might
wish. At some point we have to introduce wrappers (like W a above) to
hand over the dispatch to another class. It's possible to do the
dispatch on a class, but it's a bit too painful. OTH, the wrappers
such as 'W' may be considered as an 'alternative view' of an object.
By wrapping an object, we can switch its behavior from the main one to
an alternative without any run-time penalty.

More information about the Haskell mailing list