[Haskell-cafe] Some thoughts on Type-Directed Name Resolution

Steve Horne sh006d3592 at blueyonder.co.uk
Tue Jan 24 05:10:14 CET 2012


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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20120124/2a947f67/attachment.htm>


More information about the Haskell-Cafe mailing list