There's a proposal at the moment to add support for TDNR to Haskell - to
leverage "the power of the dot" (e.g. for intellisense).
http://hackage.haskell.org/trac/haskell-prime/wiki/TypeDirectedNameResolution
I approve of the goal, but I'd like to suggest a different approach.
My basic idea is stolen from Bertrand Meyer (Object-Oriented Software
Construction, second edition). Basically, a class *is* both a module and
a type. Quote...
Classes as modules
Object orientation is primarily an architectural technique: its major
effect is on the
modular structure of software systems.
The key role here is again played by classes. A class describes not
just a type of
objects but also a modular unit. In a pure object-oriented approach:
Classes should be the only modules.
By the logic of equivalence relations, we can conclude that a type *is*
a module. Only I'd adapt that a little. In C++, the following operators
can all be used to access the "module" for some type or value...
* :: Scope resolution
* . Member dereference
* -> Member dereference via a pointer
* .* Member-pointer dereference
* ->* Member-pointer dereference via a pointer
In C++, a type and an instance each have their own modules. A (smart)
pointer has its own module, separate from the module for the type it
points to. And member-pointers exist because sometimes there's a need to
reference a member without knowing or (yet) caring which instance.
We already have member pointers - the functions that map an instance to
the field value. It would make some sense if these could be placed in a
module associated with the type (not the instance).
When an instance is created of a type, that can effectively (without
run-time overhead) create a new module associated with the new instance.
This will contain the same field-access functions, but with the instance
parameter already curried in.
So there's no real need for any new meaning of the . operator - it's
just access to names within a module. And there's no need for a new
mechanism for accessing fields - only for a way to place them in that
module scope, and a little sugar that gives us the same field-access
function but with the instance parameter already curried in.
Once we have these modules containing compiler-generated field-access
functions, though, it makes some sense to allow additional functions
(and perhaps types) to be added within that types module explicitly by
the programmer. It may also make sense to allow functions to be
explicitly defined which will be added to the instance-modules and
support the prefix-instance-parameter sugar.
Finally, as with C++, when dealing with IORef and similar, it make make
sense to have a separate -> operator (spelled differently, of course).
Or it could use the standard dot. C++ and D disagree in this (in C++,
the smart pointer has its own module separate from the pointed-at
instance - in D, there is no -> or equivalent).
As an aside, Ada has already gone through a related transition. The
original Ada 83 had variant records, but no "true classes". In Ada 95,
"tagged types" were added which were like variant records, but which
supported inheritance and run-time dispatch. The discriminant is
replaced by a "tag" which is presumably implemented as a virtual table
pointer. However, functions and procedures weren't members. The typical
call of a "method" would be...
packagename.procedure_name ( instance_arg, other_args );
Ada 2005 added some workarounds to allow conventional OOP call notation.
See section 1.3 of the Ada 2005 rationale for details. However, it all
feels a bit kludgy. In particular, the procedures and functions still
aren't members - there are just some special rules for when they can be
used as if they were. I've not actually used Ada 2005, but I'd bet some
confusion can result from that.
Personally, I think Meyer was at least partly right - if types (and
instances) are modules, the kludge-factor is much lower. C++ actually
doesn't get this quite right IMO (you can access static class members
through the instance objects, for example, not just through the
classes), but C++ classes *do* act mostly like modules and that is a
very useful trait - particularly within the declarative sublanguage
(templates etc).
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe