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

Reply via email to