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

Simon Peyton-Jones simonpj at microsoft.com
Thu Mar 4 09:21:23 EST 2004


| detail, of course ;).  What I'm secretly hoping is that the
| GHC/hugs/HBC people will see what I'm trying to achieve, tell me I'm
| totally nuts, and then suggest an alternative, much simpler approach
| which gives us exactly the same goal ...

As I implied earlier, I am thus far unconvinced that the complexity cost
justifies the benefit.  Let me suggest two simpler alternatives.

Alternative 1
~~~~~~~~
Given 
	import FiniteMap( FM, add {- :: FM a b -> a -> b -> FM a b -} )
and 	fm :: FM Int Bool, 
you want
	fm.add x y
to mean the same as
	add fm x y
(but in addition allow lots of unrelated 'add' functions)

My objection is that the type of 'fm' is a matter of inference, whereas
in Java it'd be a matter of explicit declaration.  But you could adapt
your story to be more explicit, thus

	FM.add fm x y

The qualifier here is the *type* not the *module*.  (Let's assume they
share a name space for now.)  This would work for record selectors too:
	data T = T { x::Int, y::Int }
	data S = S { x::Int, y::Int }

Now
	f p q = T.x p + S.y q

I guess the rule is that you can qualify a function by the name of the
outermost type constructor of its first argument.

I would restrict this only to functions with explicitly-declared types,
so that there's no interaction with inference.

Alternative 2
~~~~~~~~
If the big bug-bear is record selectors, let's focus on them
exclusively.  I now think that ML got it right. ML records are simply
labelled tuples.  So just as (Bool,Int) is an anonymous type, so is
{x::Bool, y::Int}.  Indeed (Bool,Int) is just shorthand for {#1::Bool,
#2::Int}.

In Haskell, a function with a tuple argument takes exactly that tuple:
	f :: (Bool,Int) -> Bool
	f (x,y) = x
Here, f cannot take a 3-tuple or a 89-tuple.  In ML it's the same with
records (I'm not getting the ML syntax right, but you'll get the idea):
	f :: {x::Bool, y::Int} -> Bool
	f r = r.x
Here f cannot take a record of shape other than {x::Bool,y::Int}.  You
are allowed to write
	f {x, ...} = x
but you must then explain f's argument type separately.  For example you
can write
	f :: {x::Bool, p::String, q::Bool} -> Bool
	f {x, ...} = x

So there is no sub-typing, no row polymorphism, no attempt to give
	f r = r.x
a fancy type that makes f applicable to any record with an x field.  

On the other hand, there is also no problem with many records having the
same field name either, which is the problem we started with.  There are
no implicitly-defined record selectors either: you have to use pattern
matching for that.


My personal view is this: we should have adopted the ML view of records.
It solves the immediate problem, and no more elaborate scheme seems
sufficiently "right" to be declared the winner.    Alas, like all other
proposals, it's not backward compatible, and hence not likely to fly.

Simon



More information about the Haskell mailing list