[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Abstracts and Interfaces in Ruby?

Miles Keaton

12/13/2004 6:20:00 AM

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?


11 Answers

gabriele renzi

12/13/2004 7:12:00 AM

0

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

Nicholas Van Weerdenburg

12/13/2004 7:12:00 AM

0

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


Matt Mower

12/13/2004 7:32:00 AM

0

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


Robert Klemme

12/13/2004 9:35:00 AM

0


"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

Robert Klemme

12/13/2004 9:38:00 AM

0


"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

Edgardo Hames

12/13/2004 1:30:00 PM

0

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
>

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

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


Florian Gross

12/13/2004 2:10:00 PM

0

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.

Glenn Parker

12/13/2004 2:42:00 PM

0

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

Short answer: don't do 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

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


Nicholas Van Weerdenburg

12/13/2004 3:51:00 PM

0

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

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

--
Nicholas Van Weerdenburg


Robert Klemme

12/13/2004 3:57:00 PM

0


"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...
> >
> >
>
> Good point. It was not a well thought out example.

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

Regards

robert