Abstracts and Interfaces in Ruby?

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

···

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?

--
Nicholas Van Weerdenburg

Miles Keaton ha scritto:

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

generally, no. If you use Abstract classes to follow a template method approach it is ok, but we don't usually check interface definition.
Anyway, you can do it (this is ruby ;).
See the recent thread here:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/119878

"Miles Keaton" <mileskeaton@gmail.com> schrieb im Newsbeitrag
news:59b2d39b04121222201b1064c5@mail.gmail.com...

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

You'd rather throw an exception here. But this would happen anyway if the
method remained undefined and someone attempted to invoke it. So you
don't really gain much apart maybe from a different error message and rdoc
documentation for this method.

Or does Ruby have a whole different approach to this that I missed

somewhere?

Usually we don't do this in Ruby (=> "Duck Typing"). Otherwise see
Gabriele's hints.

Kind regards

    robert

Would this not introduce a breakage? IIRC the search order for
methods is object->modules->baseclass->baseclass modules.

Hence if you mixed Enumerable into an object whose base class
imlemented #each you would *hide* the base-class implementation.

Or have I got it wrong? (I'm still a learner).

Regards,

Matt

···

On Mon, 13 Dec 2004 16:12:04 +0900, Nicholas Van Weerdenburg <vanweerd@gmail.com> wrote:

On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton <mileskeaton@gmail.com > wrote:

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

--
Matt Mower :: http://matt.blogs.it/

An example posted by Michael Neumann a few days ago:

  class Module
    def abstract(*meths)
      meths.each do |meth|
        class_eval "def #{ meth }(*args, &block) raise 'abstract
method' end"
      end
    end
  end

Then, you have the "abstract" keyword you were mentioning. Sometimes I
needed subclasses of a given class to implement their own version of a
method and I didn't know how to indicate that beyond documentation.
"abstract" comes very handy.

Kind Regards,
Ed

···

On Mon, 13 Dec 2004 16:12:04 +0900, Nicholas Van Weerdenburg <vanweerd@gmail.com> wrote:

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?
>

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

--
Pretty women make us buy beer, ugly women make us drink beer

Robert Klemme wrote:

Usually we don't do this [Interfaces] in Ruby (=> "Duck Typing").
Otherwise see Gabriele's hints.

Regarding that I would still find TestSuite-based contract testing
interesting even if only for polymorphic methods.

"Matt Mower" <matt.mower@gmail.com> schrieb im Newsbeitrag
news:d563731904121223321f545b28@mail.gmail.com...

···

On Mon, 13 Dec 2004 16:12:04 +0900, Nicholas Van Weerdenburg > <vanweerd@gmail.com> wrote:
> On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton <mileskeaton@gmail.com > wrote:
>
> 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
>

Would this not introduce a breakage? IIRC the search order for
methods is object->modules->baseclass->baseclass modules.

Hence if you mixed Enumerable into an object whose base class
imlemented #each you would *hide* the base-class implementation.

Or have I got it wrong? (I'm still a learner).

I think so:

10:34:52 [robert.klemme]: ruby -e 'p Array.ancestors'
[Array, Enumerable, Object, Kernel]

Kind regards

    robert

What's the recommended Ruby way to do abstract classes and abstract
methods?

Short answer: don't do it. :slight_smile:

An example posted by Michael Neumann a few days ago:

  class Module
    def abstract(*meths)
      meths.each do |meth|
        class_eval "def #{ meth }(*args, &block) raise 'abstract
method' end"
      end
    end
  end

I thought I might be able to wrangle a compile time notification using the code below, but it doesn't work. Class#inherited is called before the derived class's methods are defined, so I cannot really examine the resulting derived class in Class#inherited. It might be nice to have a Class#post_inherited that is called at the end of the class definition.

FWIW, I do realize that methods can always be added later, so it's never too late to "fix" a missing abstract method. This is just a question about meta-programming features in general, not about how to get the perfect "abstract" method.

class Class
   def abstract(*methods)
     @abstract_methods ||=
     @abstract_methods += methods
   end
   def inherited(derived)
     if defined? @abstract_methods
       unimplemented = @abstract_methods.reject do |meth|
         derived.instance_methods(true).include? meth
       end
       if not unimplemented.empty?
         raise "class #{derived}: missing #{unimplemented.join(', ')}"
       end
     end
   end
end

class B
   abstract :a, :b
end

class D < B
   def a
   end
end

=> in `inherited': class D: missing a, b (RuntimeError)

I had hoped this would print only "class D: missing b".

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

Good point. It was not a well thought out example.

···

On Mon, 13 Dec 2004 16:32:07 +0900, Matt Mower <matt.mower@gmail.com> wrote:

On Mon, 13 Dec 2004 16:12:04 +0900, Nicholas Van Weerdenburg > > > <vanweerd@gmail.com> wrote:
> On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton <mileskeaton@gmail.com > wrote:
>
> 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
>

Would this not introduce a breakage? IIRC the search order for
methods is object->modules->baseclass->baseclass modules.

Hence if you mixed Enumerable into an object whose base class
imlemented #each you would *hide* the base-class implementation.

Or have I got it wrong? (I'm still a learner).

Regards,

Matt

--
Matt Mower :: http://matt.blogs.it/

--
Nicholas Van Weerdenburg

"Nicholas Van Weerdenburg" <vanweerd@gmail.com> schrieb im Newsbeitrag
news:632154f7041213075134ba5f70@mail.gmail.com...

···

On Mon, 13 Dec 2004 16:32:07 +0900, Matt Mower <matt.mower@gmail.com> wrote:
> On Mon, 13 Dec 2004 16:12:04 +0900, Nicholas Van Weerdenburg > > > > > > <vanweerd@gmail.com> wrote:
> > On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton <mileskeaton@gmail.com > wrote:
> >
> > 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
> >
>
> Would this not introduce a breakage? IIRC the search order for
> methods is object->modules->baseclass->baseclass modules.
>
> Hence if you mixed Enumerable into an object whose base class
> imlemented #each you would *hide* the base-class implementation.
>
> Or have I got it wrong? (I'm still a learner).
>
> Regards,
>
> Matt
>
> --
> Matt Mower :: http://matt.blogs.it/
>
>

Good point. It was not a well thought out example.

No, not a good point. See my other posting.

Regards

    robert

I misread your other posting.

Trying in irb, I see there is no breakage.

Thanks,
Nick

···

On Tue, 14 Dec 2004 00:57:20 +0900, Robert Klemme <bob.news@gmx.net> wrote:

"Nicholas Van Weerdenburg" <vanweerd@gmail.com> schrieb im Newsbeitrag
news:632154f7041213075134ba5f70@mail.gmail.com...

> On Mon, 13 Dec 2004 16:32:07 +0900, Matt Mower <matt.mower@gmail.com> > wrote:
> > On Mon, 13 Dec 2004 16:12:04 +0900, Nicholas Van Weerdenburg > > > > > > > > > <vanweerd@gmail.com> wrote:
> > > On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton > <mileskeaton@gmail.com > wrote:
> > >
> > > 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
> > >
> >
> > Would this not introduce a breakage? IIRC the search order for
> > methods is object->modules->baseclass->baseclass modules.
> >
> > Hence if you mixed Enumerable into an object whose base class
> > imlemented #each you would *hide* the base-class implementation.
> >
> > Or have I got it wrong? (I'm still a learner).
> >
> > Regards,
> >
> > Matt
> >
> > --
> > Matt Mower :: http://matt.blogs.it/
> >
> >
>
> Good point. It was not a well thought out example.

No, not a good point. See my other posting.

Regards

    robert

--
Nicholas Van Weerdenburg