Abstracts and Interfaces in Ruby?

M

Miles Keaton

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

Nicholas Van Weerdenburg

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
 
G

gabriele renzi

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
 
M

Matt Mower

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
 
R

Robert Klemme

Matt Mower said:
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
 
R

Robert Klemme

Miles Keaton said:
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
 
E

Edgardo Hames

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
 
F

Florian Gross

Robert said:
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.
 
G

Glenn Parker

What's the recommended Ruby way to do abstract classes and abstract
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".
 
N

Nicholas Van Weerdenburg

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

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top