From: Geoff Youngs Date: 2003-11-21T10:34:30+09:00 Subject: Re: New Type Checking System Idea On Fri, Nov 21, 2003 at 06:08:39AM +0900, Sean O'Dell wrote: > Taking comments into consideration, a totally new approach strikes me > regarding type checking. > As briefly as I can: > What if we had a way to describe a class interface that basically does > this: "from this point forward, any methods added to this class > definition will be checked against an interface description, and any > methods whose names matches a method described in the interface must > take the number and type of parameters described, and the class will > be considered incomplete until the very last method described by the > interface is added to the class." > *** Some basic rules about this: > Classes do not *need* an interface. They can go on just the way the > are now, completely open and free. For someone who doesn't like to > use interfaces, they are completely unaffected. > Interfaces are immutable and cannot be extended, but you can subclass > other interfaces from them. Why? Is there any reason that interfaces shouldn't be as mutable as classes and/or modules are? I've played with a soft interface implementation which works reasonably well where I've used it, but it's worth considering existing ruby usage. And the most common example I come across of a Ruby interface is Enumerable. The problems (as I see it) with the current implementation of Enumerable are that: 1/ It doesn't check for the existance of 'each' until you call a method. 2/ It has brute force methods which could be implemented more efficiently in specialised collections, e.g. Range, or a sorted array. 3/ It supports reflection poorly. ie. there's no easy way to look at Enumerable and work out what a class needs as a pre-requisite in order to use Enumerable usefully. 1/ Could be solved using Iface.append_features(aClassOrModule) to check that the class defines 'each' as an instance method. 3/ Could be solved by making the interface keep track of the required methods (which would help generically solve 1) 2/ Could be solved by allowing the interface to provide methods which don't override class methods of the same name. There are also non-functional interfaces: e.g. Marshal requires _dump and class._load to be defined. The advantage of using a specified interface is that duck-typing isn't enough. And as language doesn't have a infinite list of suitable methods names, there's the chance that there may well be a clash. e.g. class AmericanClothes attr_reader :pants end class BritishPerson def put_on_underwear(clothesSource) if @underwear.nil? if clothesSources.respond_to?(:pants) put_on(clothesSource.pants) else raise ClothingError, "No suitable underwear" end end end end Which is a tongue in cheek example, but the point is that there is no universal atomic source of method names. But an interface could exist in a global namespace, and thus guarantee (as much as anything does in Ruby at the moment) that method :x does what is expected of it, because the class declares it's conformance. Another reason for a clear interface definitions which I haven't noticed being mentioned is that for some purposes libraries need types which are more complex than Ruby's simple builtin types. The library will either need a large number of respond_to? calls or a simple "does it claim to conform to interface X?". The advantages of interfaces as proposed are twofold - for the programmer a clear definition of the expectations as regard a complex object, for the library a simple check for conformance. And regardless of what some detractors have said, I do believe that this adds rather than detracts from the dynamism of Ruby - so much Ruby code checks for kind_of?(IO) when it really means implements?(IORead). With interfaces this could and should be clearer and promote better programming practices. > Interfaces have unique, global names based on where they were defined. > They take the name of the class or module they were described in, but > they are not otherwise related to the class or module. Aren't/Couldn't they be a specialised module? > Interfaces can be assigned to any class regardless of its proximity to > the interface description. > Once a class has been assigned an interface, it's an error to change > the number or types of parameters described methods take. Isn't this an unlikely contingency to cater for? And more trouble than it's worth? If there's a library with an interface which accepts string arguments to a particular methods, for example. --- in foo.rb interface Foo def bar(String title) end --- in myprog.rb class Bob implements Foo def bar(String|IORead title) ... end end Couldn't the programmer end up in the situation where they have to modify a library (and either hope that the change is accepted by the library author or distribute a modified version of the library) so that they can have a class which behaves as they wish when used by their code, but which is also accepted by the library? With dynamic interface definitions this sort of thing could be worked around, but if they were immutable then every application author would have to rely on every interface using library author anticipating all possible uses of their library. > You can still add methods any way you like, so long as they're not > part of the described interface for the class. See above. > When you subclass from a class with an interface, or mix-in a module > that has an interface, the subclass has that interface as well. This is pretty much essential, but you'd get it "for free" if interfaces were specialised modules. > You can re-implement methods in a subclass which has inherited an > interface, but the methods must have the same number and types of > parameters. Ok - but I still don't see why a program that knows what it's doing can't take a class and use alias_method to insert hooks in a particular function. > *** Syntax sugar > Parameter and return types are interface names. > If a method needs to enforce a parameter type check, objects passed to > that parameter must contain a "complete" flag for the interface > required. Nice, but automated parameter type checking is not essential for a basic interface implementation. [snip] > Anything wrong with this way of doing it? The main disadvantages I see are as follows: 1/ Compatibility Libraries using this will not be usable earlier versions of Ruby. As in they will break completely or displayed undefined behaviour - this is a Bad Thing(TM) and will damage uptake. (The great thing about so many of the changes between 1.6 and 1.8 is that many of them can be utilised but 1.6 still supported by means of a compatibility layer written in Ruby - (I, for example, still have a machine running 1.6 which I'm not in a position to upgrade at the moment - hence pretty much anything I write in Ruby for work needs to run on both). 2/ Dynamic extensibillity I have a big problem with making interfaces immutable - it just doesn't feel right in Ruby. And greater certainty that the object isn't lying seems to be the only reason given for doing it. (Ignoring the fact that the Object could be lie about the return value of kind_of? anyway) 3/ Core interpreter There are already various "core" features implemented in pure Ruby - the most prominent being the Singleton mix-in. I don't see evidence supporting the notion that unless it's done in Ruby's C source, interfaces won't/can't become a meaningful and useful tool - I would agree though that they are unlikely to have significant penetration unless an implementation is chosen and included in the core distribution. Hope this is of interest, Geoff.