On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton <mileskeaton / gmail.com> wrote:
> What's the recommended Ruby way to do abstract classes and abstract methods?
> 
> Related : what's the Ruby way for interfaces?
> 
> While Googling for it, I was thinking about what abstracts and
> interfaces are used for in Java and PHP5 ... and it came down to
> stopping with errors if a certain method was NOT implemented.
> 
> So... is that kind of thing just not the Ruby Way?  (To give you
> errors for not doing something.)
> 
> Would I just do workarounds to say:
> 
> class AbstractSomething
>   def method
>     puts "error - abstract!"
>   end
> end
> 
> Or does Ruby have a whole different approach to this that I missed somewhere?
> 
> 

Ruby has modules that can be mixed in to classes- a cleaner multiple
inheritence that is not technically multiple inheritence.

A good example is the Enumerable module- you mix it in to your classes
to gain it's functionality. But interestingly, the most important
method, each, isn't defined in the module and must be implemented in
your class for Enumerable to work.

I originally thought it would be nice to have a warning or some
indication, but in Ruby there isn't concept of a contracts though that
says "you must implement this"- aka interfaces or abstract base
classes.  One reason I suppose is that everything in Ruby is very
dynamic, and it would be really hard to figure out pre-runtime wether
or not a method had been implemented. For instance, it might be that a
class uses method_missing to intercept the method call, and implement
the method dynamically.

Similarly, it's possible to undef methods. Thus it would be an awkward
paradigm in Ruby- it is contrary to it's dynamic nature.

But, while Ruby doesn't have the same concept, it's metaprogramming
capabilities make it fairly easy to implement- though many would
disagree with the goal behind that. And note that the error would be
at run-time.

For example, you can extend the Enumerable modules, and your changes
apply to all objects in the system that use Enumerable:

module Enumerable
 def each
     raise "you must implement each" 
 end
end

So now you get a more specific error, and have a contract in place,
albeit only at run-time.

So you could do something like:

module MyInteface
  def method1
      raise "you must implement method1"
  end

  def method2
     raise "you must implement method2"
  end
end

or extend Object to have a "abstract" keyword that does the boiler
plate for you (the method definitions with the exception). Then your
module looks like this:

module MyInterface
   abstract :method1, :method2, :method3
end

In either case, you then use it like this-
class MyClass
  include MyInterface
  ...
end


While not complile type checking, it's a much more clear error then
"undefined method". The first time code looks for the unimplemented
method, there is a clear message.

There are some cleverer things that could surely be done to find the
error earlier- maybe when MyClass is defined, but they are a bit
beyond my ruby experience.

BTW- there is a library called "cs/interface", available as a ruby gem
"csinterface". Search the newsgroup for that, and you'll find a lot of
discussion. Many disagreed with the need, but the great thing about
Ruby is it's flexibility.

In general, unit testing is seen as a sufficient replacement for the
benefits of typing.  Once you have unit testing, you have such a
focused evaluation of your codes contracts (implied) and behaviour
that explicit support for types and interfaces is not worth the loss
of dynamic behaviour.

Regards,
Nick
-- 
Nicholas Van Weerdenburg