[Haskell-cafe] a bunch of newbie questions

Brian Hulley brianh at metamilk.com
Fri Aug 4 08:46:22 EDT 2006


Mark T.B. Carroll wrote:
> Janis Voigtlaender <voigt at tcs.inf.tu-dresden.de> writes:
> (snip)
>> Yes, as long as enough type information is provided for the
>> typechecker to decide what is the correct instance to use.
> (snip)
>
> I've always been a little surprised when this doesn't happen more
> widely for things other than instances. For instance, when
> IntMap.size, Map.size and Set.size (or whatever) are all in scope as
> "size", it should be fairly obvious what (size x) is about once we've
> inferred, for other reasons, that x is an IntMap. Similarly with
> records, if we had field names that cause functions for extracting
> the value of those fields, where we used the same field name in two
> different record types, I figure that there's usually no ambiguity
> because we can usually infer the type of the object to which the
> 'extractor' is being applied.
>
> Am I just not seeing the big picture, or would people not find this
> use of type information to resolve such ambiguities as nice as I
> would, or is it harder to do that than I'm expecting?

I think this is because in Haskell the only way to overload function names 
is to use type classes and instances just as the one and only way to qualify 
an identifier is by using modules. This has the advantage that different 
concerns are clearly separated out and dealt with in exactly one place by 
one concern-specific mechanism.

Perhaps the basic problem is that (size) really belongs in a type class and 
IntMap, Set, Map etc were created before anyone bothered to try and factor 
their portions of common functionality into type classes. This factoring is 
a non-trivial problem however (as you can see from the various posts on the 
subject of sequences) since the design space is not nearly as well 
understood as basic mathematical objects like monoids, monads etc and 
without a mathematical foundation it is difficult to design a type class 
factoring with any confidence.

For record fields, I suggested a while back that the compiler could 
automatically create the relevant type classes and instances eg:

        data Point i = Point {x :: i}

would ensure that a global typeclass was created if not already present:

        class (.x) a b | a -> b where
            (.x) :: a -> b

and would also create an instance for that type:

        instance (.x) (Point i) i where
            (.x) (Point k) = k

where ".x" is parsed as a single lexeme and treated as a postfix operator 
ie:

        -- no space between '.' and fieldname
        exp .x or exp.x === (.x) exp

but
        exp . x === (.) exp x -- composition

To refer to a particular field name directly you could use:

        g = ((.x) :: Point a -> a)

but I also thought it might be nice to have a special syntax to make things 
less clunky eg:

        g = Point^x

(It could not be written as Point.x because Point is not a module name, 
unless you wanted to destroy the very simple rule that Foo.xyz qualifies xyz 
by the module Foo)

In any case with the trivial syntactic sugar above it would already be 
possible to use the same record names in multiple types simultaneously. I 
think the reason there was no positive feedback about this idea before is 
that unfortunately it looks as if the record system is to be extended to 
deal with subtyping or horrible anonymous records such as {x=5} that don't 
have an explicit value constructor in front of them instead of just 
concentrating on making the existing system (which I really like apart from 
the lack of field name overloading and dot access syntax) more usable as 
above.

For value constructors, a change in the type inference algorithm would be 
needed if they were to be resolved based on their types so you could write 
eg:

     data Location = Left | Right | Up | Down
     data Hand = Left | Right

     foo :: Location -> Hand
     foo Left = Left
     foo Up = Left
     foo Right = Right
     foo Down = Right

and again there could be a syntax like Location^Left as a less clunky 
alternative to (Left::Location) in cases where ambiguity needs to be 
resolved explicitly eg if someone didn't want to write a top level type 
signature for their function.

Imho this would make life a lot easier because you could concentrate on 
names that are appropriate to the type without having to know about all the 
other types you'd want to use unqualified in the same scope.

Regards, Brian.
-- 
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 



More information about the Haskell-Cafe mailing list