Abstract method in Ruby


#1

To define abstract method in Ruby, I use NotImplementedError like the
following:


class Foo
def m1
raise NotImpelemntedError("#{self.class.name}#m1() is not
implemented.")
end
end

I think it is convenient if the Module#abstract_method is defined.


class Module
def abstract_method(*method_names)
method_names.each do |name|
s = <<-END
def #{name}
mesg = “#{self.class.name}##{name}() is not implemented.”
raise NotImplementedError.new(mesg)
end
END
module_eval s
end
end

class Foo
abstract_method :m1, :m2, :m3 # define abstract methods
end

obj = Foo.new
obj.m1 #=> Foo#m1() is not implemented yet. (NotImplementedError)

But this solution doesn’t allow us to define a method with arguments.
The following is another solution to define abstract method
which is able to define a method with arguments.

? example.rb

module Abstract # or Module?
def not_implemented # or should_be_implemented?
backtrace = caller()
method_name = (backtrace.shift =~ /`(\w+)’$/) && $1
mesg = “#{self.class.name}##{method_name}() is not implemented.”
err = NotImplementedError.new(mesg)
err.set_backtrace(backtrace)
raise err
end
end

class Foo
include Abstract
def m1(arg)
not_implemented
end
end

obj = Foo.new
p obj.m1(‘abc’) #=> example.rb:20: Foo#m1() is not implemented.
(NotImplementedError)

I think this is useful for everyone and I hope it will be included in
Ruby.
Could you give me any advices?


#2

On 3/11/06, kwatch removed_email_address@domain.invalid wrote:

To define abstract method in Ruby, I use NotImplementedError like the
following:

In the last four years of using Ruby, I thought I needed abstract
methods in the first few months. Then I learned what value Modules as
mixins gave me.

I do not believe that this is a common enough need that it needs to be
in the core of Ruby.

I encourage you to do what others have done with this sort of
mismatched feature and make a library that implements it and make it
available for use. Your pure-Ruby implementation looks more than
sufficient.

More than that, though, I encourage you to rethink why you need
abstract methods. Most of the time this is because you’re thinking in
terms of C++ or Java inheritance, when Ruby’s mixins are both more
powerful and applicable in most cases where you would define a
hierarchy that has abstract methods.

-austin


#3

Thanks Austin,

Austin Z. wrote:

In the last four years of using Ruby, I thought I needed abstract
methods in the first few months. Then I learned what value Modules as
mixins gave me.

I do not believe that this is a common enough need that it needs to be
in the core of Ruby.

Well, I think that abstract method is more basic than ‘singleton.rb’
or ‘delegate.rb’ which are bundled in Ruby.

I encourage you to do what others have done with this sort of
mismatched feature and make a library that implements it and make it
available for use. Your pure-Ruby implementation looks more than
sufficient.

I’ll make it library and register it to RubyForge.

More than that, though, I encourage you to rethink why you need
abstract methods. Most of the time this is because you’re thinking in
terms of C++ or Java inheritance, when Ruby’s mixins are both more
powerful and applicable in most cases where you would define a
hierarchy that has abstract methods.

In my opinion, ‘mixin’ is one thing and ‘abstract method’ is another.
Mixin doesn’t cover abstract method.

The following is an example of visitor pattern.
It shows that mixin doesn’t cover abstract method.


module Visitor
def visit_foo(acceptor) # abstract method
mesg = “#{self.class.name}#visit_foo() is not implemented.”
raise NotImplementedError.new(mesg)
end

def visit_bar(acceptor) # abstract method
mesg = “#{self.class.name}#visit_foo() is not implemented.”
raise NotImplementedError.new(mesg)
end
end

class MyVisitor
include Visitor # mix-in

def visit_foo(acceptor)
puts “visit_foo() called.”
end

def visit_bar(acceptor)
puts “visit_bar() called.”
end
end

class Foo
def accept(visitor)
visitor.visit_foo(self)
end
end

class Bar
def accept(visitor)
visitor.visit_bar(self)
end
end

Ruby’s mix-in is more sophisticated solution than interface of Java
or multiple inheritance of C++, I think.
But mix-in and abstract method are different thing.


#4

On Mar 11, 2006, at 5:23 PM, kwatch wrote:

in the core of Ruby.
I’ll make it library and register it to RubyForge.

def visit_bar(acceptor) # abstract method
end
end
But mix-in and abstract method are different thing.


regards,
kwatch

I think you’re over engineering. Let’s consider the Enumerable module
for instance. It has an “abstract” method of sorts, each.

class A
include Enumerable
end

a = A.new
a.map { |x| x + 1 }

Oh look, this code already raises an exception, a NoMethodError. Now
I know that to include Enumerable I have to have an each method.
That’s all there is to it. Look at your Visitor example for instance,
in ruby there’s not even a need for the Visitor module, all you have
to do is implement visit_foo and visit_bar in MyVisitor. In ruby, no
one cares who your parents were, all they care about is if you know
what you are talking about.

class MyVisitor
def visit_foo(acceptor)
puts “visit_foo() called.”
end

def visit_bar(acceptor)
puts “visit_bar() called.”
end
end

class Foo
def accept(visitor)
visitor.visit_foo(self)
end
end

class Bar
def accept(visitor)
vistor.visit_bar(self)
end
end

Bam! Same thing as your code, will still raise the same exceptions,
but ONLY if you really need them. Start thinking like a duck man. Ask
yourself what is the purpose of an abstract method. Then ask yourself
if that need is already fufilled in ruby.


#5

On 11 Mar 2006, at 22:58, Logan C. wrote:

mixins gave me.
mismatched feature and make a library that implements it and make it
powerful and applicable in most cases where you would define a
def visit_foo(acceptor) # abstract method
class MyVisitor

end

Oh look, this code already raises an exception, a NoMethodError.
Now I know that to include Enumerable I have to have an each method.

:slight_smile: True, yes. For documentation though, it’s nice to know that you’re
supposed to implement something. You could point that out with a
comment of course. But a comment wouldn’t be picked up by some
hypothetical parser that’s looking at your code (not just to execute
it) and trying to deduce things about it… :slight_smile: I think I like the
idea of a declaration for an abstract methods because it makes clear
your intention.


#6

On 3/11/06, kwatch removed_email_address@domain.invalid wrote:

I think it is convenient if the Module#abstract_method is defined.

I’m of a similar opinion with others that predeclaring an abstract
method that raises NotImplementedError is of limited use when compared
with the existing NoMethodError duck-typing approach. As such, I don’t
think it belongs in the core.

However, this is a useful concept which could be used to extend the
capabilities of the language for those who do still want it. I
encourage you to build a library from this idea and publicize it. Just
because it doesn’t belong in the core doesn’t mean it won’t be useful
to some. One place I can see this being used is if the
NotImplementedError gave a more descriptive output, such as “#{self}
requires that the host implement the method ‘#{method_name}’.” E.g.
“Enumerable requires that the host implement the method ‘each’.”

    module_eval s

But this solution doesn’t allow us to define a method with arguments.

One thing that you can do to make this approach (which seems cleaner
and simpler than the backtrace manipulation approach you later
proposed) more flexible by removing the arity restriction on the
method using the splat operator:

$ cat abstract.rb
class Module
def abstract_method(*method_names)
mesg_template = “#{self} requires that the host implement the
method ‘%s’.”
method_names.each do |name|
mesg = mesg_template % [name]
module_eval <<-END
def #{name}(*args)
raise NotImplementedError.new("#{mesg}")
end
END
end
end
end

module MyModule
def foo
bar(“test”)
end

abstract_method :bar

end

class MyClass
include MyModule
end

a = MyClass.new
b = MyClass.new

class << a
def bar( s )
puts s
end
end

a.foo
b.foo

$ ruby abstract.rb
test
(eval):2:in bar': MyModule requires that the host implement the method 'bar'. (NotImplementedError) from abstract.rb:17:infoo’
from abstract.rb:37


#7

Logan,

Oh look, this code already raises an exception, a NoMethodError. Now
I know that to include Enumerable I have to have an each method.
That’s all there is to it.

I think it is very important that the code itself is descriptive.
The following code doesn’t describe that method each() is abstract.

module Enumerable
def map
arr = []
each { |elem| arr << yield(elem) }
arr
end
end

The following code itself describes that method each() is abstract.
It’s more descriptive.

module Enumerable
abstract_method :each
def map
arr = []
each { |elem| arr << yield(elem) }
arr
end
end

Please take care of reading code, as well as writing code.
You may insist that documentation is sufficient to describe that,
but documentation is assistant and code should be prime.

Oh look, this code already raises an exception, a NoMethodError.

NoMethodError is not proper to abstract method, I think.

Look at your Visitor example for instance,
in ruby there’s not even a need for the Visitor module, all you have
to do is implement visit_foo and visit_bar in MyVisitor. In ruby, no
one cares who your parents were, all they care about is if you know
what you are talking about.

Assume that you need to define many visitor classes.

module Visitor
abstract_method :visit_foo, :visit_bar
end

class Hoge
include Visitor
def visit_foo; … ; end
def visit_bar; … ; end
end

class Fuga
include Visitor
def visit_foo; … ; end
def visit_bar; … ; end
end

class Geji
include Visitor
def visit_foo; … ; end
def visit_bar; … ; end
end

The above code itself describes that “class Hoge, Fuga, and Geji
are visotr classes” very clearly.
‘Visitor’ module may not be necessary when a number of visitor class
is only 1.
But there should be ‘Visitor’ module in the case that many visitor
classes are needed. It’s more descriptive.

Start thinking like a duck man.

You mean ‘duck typing’?
Duck typing is one thing and abstract method is another.

Ask yourself what is the purpose of an abstract method.
Then ask yourself if that need is already fufilled in ruby.

Abstract method is useful to realize ‘descriptive code’.
It increases code maintanancability.


regards,
kwatch


#8

Jacob F. wrote:

I’m of a similar opinion with others that predeclaring an abstract
method that raises NotImplementedError is of limited use when compared
with the existing NoMethodError duck-typing approach. As such, I don’t
think it belongs in the core.

Hmm…
IMO, abstract method is more useful than ‘singleton.rb’ or
‘delegate.rb’.
It’s just my opinion.

However, this is a useful concept which could be used to extend the
capabilities of the language for those who do still want it. I
encourage you to build a library from this idea and publicize it. Just
because it doesn’t belong in the core doesn’t mean it won’t be useful
to some.

Yes, I’m going to release ‘abstract.rb’.

One place I can see this being used is if the
NotImplementedError gave a more descriptive output, such as “#{self}
requires that the host implement the method ‘#{method_name}’.” E.g.
“Enumerable requires that the host implement the method ‘each’.”
…(snip)…
One thing that you can do to make this approach (which seems cleaner
and simpler than the backtrace manipulation approach you later
proposed) more flexible by removing the arity restriction on the
method using the splat operator:

Thaks for your good advice.
I changed Module#abstract_method() like the following:


class Module
def abstract_method args_str, *method_names
method_names.each do |name|
mesg = “class %s should implement abstract method
`#{self.name}##{name}()’.”
module_eval <<-END
def #{name}(#{args_str})
err = NotImplementedError.new(mesg % self.class.name)
err.set_backtrace caller()
raise err
end
END
end
end
end

Manipulating backtrace of exception is need in order to show
linenumber in which an abstract method is invoked.

example:


require ‘abstract’
class Foo
abstract_method ‘*args’, :m1, :m2 # removing the arity
restriction
end
class Bar < Foo
def m1; puts “Foo#m1() called.”; end
end
obj = Bar.new
obj.m1 #=> “Foo#m1() called.”
obj.m2 #=> ex.rb:10: class Bar should implement abstract method
`Foo#m2()’. (NotImplementedError)


regards,
kwatch


#9

On 3/12/06, kwatch removed_email_address@domain.invalid wrote:
[…]

Duck typing is one thing and abstract method is another.

hi kwatch,
if you want serious discussion then it would be great if you were able
to
tell us why you think so.
a statement (alone) is nothing worth if it is not supported by
reasonable
arguments.

– henon


#10

On Mar 12, 2006, at 2:38 AM, kwatch wrote:

I think it is very important that the code itself is descriptive.
The following code doesn’t describe that method each() is abstract.

I think then we have a difference of opinion of the meaning and usage
of abstract methods.
I am of the opinion that they exist to enforce stronger typing,
especially at compile time. It’s a solution to the problem of having
a class we’re the implementation of a given method is given to a
child class but all methods available to a class must be known at
compile time. It’s especially helpful when the abstract method must
return something, and there is no sane default.

class A {
int someAbstractMethod( ) {
// If I don’t have abstract methods, what should I return?
// whatever I return, it won’t be correct and it won’t
force child classes
// to implement me
}
}

OTOH in ruby since it’s dynamically typed, the compiler won’t
complain about undefined methods until they actually get called (if
they are still undefined)

eg
class A {
someOtherMethod( ) {
x = someAbstractMethod( ); // forces child classes to
implement it simply by calling it
}
}

Now if you want to have an argument about static vs. dynamic typing
that’s one thing. You say that it’s important that code be
descriptive. You also say that documentation is insufficient. I
disagree, and say no code is possibly descriptive enough. Whether
attempting to call a method
that relies on an “abstract” method raises a NoMethodError or a
NotImplementedError the result is the same. The programmer must go
read the documentation (or possibly the calling code) to figure out
what was expected of him.

Basically, I feel abstract methods are a primarily feature of static
typing and help ensure the correctness of your code. But they are a
typing feature and the error message you get from them isn’t any more
descriptive than “function max expected int, got string”

Tangentially related to this, I find your Visitor example a little
weak, since your Visitor module provides no methods of its own.
Modules are not Java interfaces, and the only reason to write
“include Visitor” in your code is to say

this class implements the methods required to visit other objects

I think you’ve turned the idea of descriptive code into a degenerate
case of ALL this code does is describe. It has no functional purpose.
It’s like using no-ops to spell out documentation in morse-code :slight_smile:
(admittedly, that analogy is a bit ridiculous).

Feel free to ignore the previous paragraph by saying something along
the lines of “Well of course a real visitor module would have
additional methods, it was just an example.” :slight_smile:


#11

To define abstract method in Ruby, I use NotImplementedError
like the following:

You can’t call them abstract methods if you implement them
like this.

In Java, the presence of methods is checked at compiletime, if
they are defined “abstract”. Your solution only checks the
presence of these methods at runtime, which is already checked
by Ruby itself.

So, what functionality or checks are you really adding?

gegroet,
Erik V. - http://www.erikveen.dds.nl/


#12

On 12 Mar 2006, at 20:48, Austin Z. wrote:
snip

end

Except that it actually adds a little code for what is essentially a
documentation annotation. As I said, it adds little to no value to
Ruby
and IMO doesn’t belong in the core.

I still think that kwatch was on to something. It does do more than
merely document that the method should be implemented. It describes
intention, and provides a uniform and programatic means of doing so.
As well as filling in the default function, it could perhaps be used
to fill in rdoc generated documentation such that you get a warning
about all methods that implementations must provide. Rather than
every class that has abstract methods needing to write that
documentation and maintain it, it needs to be done once. It’s DRYer.
I’m sure there are other uses it could be put to, such as interfacing
with a static checker if you wanted to build one.

:slight_smile: I’m not saying that I’d necessarily use it either, but I think the
idea has merit. It sounds like he’s not been put off anyway. If it
turns out to be useful over a period of use, and is adopted by some
other users, then that’s all good and splendid. It’s good that people
are playing with different ideas that could be useful - it saves me
needing to.


#13

Hi,

In message “Re: [RCR] abstract method in Ruby”
on Sun, 12 Mar 2006 16:38:44 +0900, “kwatch” removed_email_address@domain.invalid
writes:

|The following code itself describes that method each() is abstract.
|It’s more descriptive.
|
| module Enumerable
| abstract_method :each
| def map
| arr = []
| each { |elem| arr << yield(elem) }
| arr
| end
| end

I’m afraid that it doesn’t work for some cases, for example:

class Base
def each

end
end

class Derived < Base
include Enumerable

end

The abstract “each” method defined in Enumerable overrides the “each”
in the Base class. I’m not against the idea of abstract (or deferred)
method, but sometimes it’s not that simple.

						matz.

#14

On 3/12/06, kwatch removed_email_address@domain.invalid wrote:

Logan,

Oh look, this code already raises an exception, a NoMethodError. Now
I know that to include Enumerable I have to have an each method.
That’s all there is to it.
I think it is very important that the code itself is descriptive. The
following code doesn’t describe that method each() is abstract.

module Enumerable
def map
arr = []
each { |elem| arr << yield(elem) }
arr
end
end

Actually, the method #each is not abstract in Enumerable. It’s a
prerequisite. There’s a difference. In the C++ and Java world, it’s
necessary to define a prerequisite method as an abstract method,
because without a method definition, the compiler goes stupid and can’t
find anything and won’t then compile your code at all. Ruby sees the
call to #each with a block and says “okay, I’ll trust that you’ve
satisified this until I hit it at run time.”

The following code itself describes that method each() is abstract.
It’s more descriptive.

module Enumerable
abstract_method :each
def map
arr = []
each { |elem| arr << yield(elem) }
arr
end
end

Please take care of reading code, as well as writing code. You may
insist that documentation is sufficient to describe that, but
documentation is assistant and code should be prime.

What you’ve got is no better than:

Every method provided in Enumerable requires that the including

class defines an #each method that iterates one item at a time.

module Enumerable
def map
arr = []
each { |elem| arr << yield(elem) }
arr
end
end

Except that it actually adds a little code for what is essentially a
documentation annotation. As I said, it adds little to no value to Ruby
and IMO doesn’t belong in the core.

Oh look, this code already raises an exception, a NoMethodError.
NoMethodError is not proper to abstract method, I think.

I think that it is. It says that there’s no method for what’s being
looked for. Having “abstract_method :each” doesn’t add value to that.

end
def visit_bar; … ; end
‘Visitor’ module may not be necessary when a number of visitor class
is only 1.
But there should be ‘Visitor’ module in the case that many visitor
classes are needed. It’s more descriptive.

Disagree. In Ruby, inclusion of a Module means something. Specifically,
it means that you’re mixing in functionality … not that you’re mixing
in warnings or merely documenting things.

# Items of class Hoge can be visited by classes that know how to
# #visit_foo or #visit_bar.
class Hoge
  def visit_foo; ... ; end
  def visit_bar; ... ; end
end

# Items of class Fuga can be visited by classes that know how to
# #visit_foo or #visit_bar.
class Fuga
  def visit_foo; ... ; end
  def visit_bar; ... ; end
end

# Items of class Geji can only be visited by classes that know how
# to #visit_foo.
class Geji
  def visit_foo; ... ; end
end

Start thinking like a duck man.
You mean ‘duck typing’? Duck typing is one thing and abstract method
is another.

Not really. As I noted earlier, abstract methods are necessary in
statically typed and linked languages because they’re aggressive in
resolving “facts”, whereas Ruby is lazy and doesn’t try to resolve
things until they’re needed.

Ask yourself what is the purpose of an abstract method.
Then ask yourself if that need is already fufilled in ruby.
Abstract method is useful to realize ‘descriptive code’.
It increases code maintanancability.

Disagree. IME, it does the opposite and reduces the descriptiveness and
usefulness of the code. Stop thinking in C++ – in Ruby, abstract
methods simply aren’t necessary.

Trust me. You won’t find them in Rails, PDF::Writer, or any of a variety
of other large and popular packages.

-austin


#15

On Mon, 13 Mar 2006, Yukihiro M. wrote:

| def map

method, but sometimes it’s not that simple.
how about something very simple? i use this in my own code

class Module
def abstract_method m
define_method(m){|*a| raise NotImplementedError}
end
end

module Interface
abstract_method “foo”
abstract_method “bar”
end

class C
include Interface
end

it’s quite nice for the rdocs.

regards.

-a


#16

On Mon, Mar 13, 2006 at 09:16:30AM +0900, removed_email_address@domain.invalid wrote:

end

end

module Interface
abstract_method “foo”
abstract_method “bar”
end

class C
include Interface
end

It fails in the very way matz indicated.

class Module
def abstract_method m
define_method(m){|*a| raise NotImplementedError}
end
end

module Interface
abstract_method :foo
def bar; foo end
end

class C
def foo; “C#foo” end
end

class D < C
include Interface
end

D.new.bar

~> -:3:in `foo’: NotImplementedError (NotImplementedError)

~> from -:9:in `bar’

~> from -:20


#17

On Mon, 13 Mar 2006, Mauricio F. wrote:

def bar; foo end
D.new.bar

~> -:3:in `foo’: NotImplementedError (NotImplementedError)

~> from -:9:in `bar’

~> from -:20

indeed. hmmm:

harp:~ > cat a.rb
class Module
def abstract_method m
define_method(m){|*a| super rescue raise NotImplementedError}
end
end
module Interface
abstract_method :foo
def bar; foo end
end
class C
def foo; “C#foo” end
end
class D < C
include Interface
end
p D::new.foo

harp:~ > ruby a.rb
“C#foo”

though i only now thought of that - so there’s probably another failing
case…

probably don’t want a blanket rescue for one…

regards.

-a


#18

I released abstract.rb 1.0.0.
http://rubyforge.org/projects/abstract/

‘abstract.rb’ is a library which enable you to define abstract method
in Ruby.

The followings are examples:

example1. (shorter notation)

require ‘rubygems’ # if installed with ‘gem install’
require ‘abstract’
class Foo
abstract_method ‘arg1, arg2=""’, :method1, :method2, :method3
end

example2. (RDoc friendly notation)

require ‘rubygems’ # if installed with ‘gem install’
require ‘abstract’
class Bar
# … method1 description …
def method1(arg1, arg2="")
not_implemented
end
# … method2 description …
def method2(arg1, arg2="")
not_implemented
end
end

Abstract method makes your code more descriptive.
It is useful even for dynamic language such as Ruby.


#19

Logan C. wrote:

Now if you want to have an argument about static vs. dynamic typing
that’s one thing.

I have no intention to argue about dynamic vs. static.
My opinion is that the concept ‘abstract method’ is useful
in both dynamic and static language.
Static language may have much merits of abstract method
than dynamic language, but the concept of abstract method
give merits to dynamic language, too.

You say that it’s important that code be
descriptive. You also say that documentation is insufficient. I
disagree, and say no code is possibly descriptive enough. Whether
attempting to call a method
that relies on an “abstract” method raises a NoMethodError or a
NotImplementedError the result is the same. The programmer must go
read the documentation (or possibly the calling code) to figure out
what was expected of him.

What you say is right. I agree with your opinion.
My point is that program code should be descriptive and
it should be prime documentation than comment or api doc.
Abstract method helps this.

Basically, I feel abstract methods are a primarily feature of static
typing and help ensure the correctness of your code. But they are a
typing feature and the error message you get from them isn’t any more
descriptive than “function max expected int, got string”

I separate ‘abstract method’ and ‘typing’. They are different thing, I
think.
I have no intention to bring strong typing into Ruby.
The merits of abstract method is not only typing.
Please take account of reading code, as well as writing or executing
code.
Descriptive code help those who reading the program code.

Tangentially related to this, I find your Visitor example a little
weak, since your Visitor module provides no methods of its own.
Modules are not Java interfaces, and the only reason to write
“include Visitor” in your code is to say

this class implements the methods required to visit other objects

Hmm, how about the template-method pattern?

module Template
def setup
# default: nothing
end

def teardown
  # default: nothing
end

abstract_method :main

def execute
  setup()
  main()
  teardown()
end

end

def Foo
include Template

def main
  # do something
end

def setup
  # ...
end

end

obj = Foo.new
obj.execute

I think you’ve turned the idea of descriptive code into a degenerate
case of ALL this code does is describe. It has no functional purpose.
It’s like using no-ops to spell out documentation in morse-code :slight_smile:
(admittedly, that analogy is a bit ridiculous).

Again, please take account of ‘readability’ of program code.
Higher readability brings higher maintainancability and lower cost.
The aim of abstract method is not to provide concrete functionality.


#20

Erik V. wrote:

You can’t call them abstract methods if you implement them
like this.
In Java, the presence of methods is checked at compiletime, if
they are defined “abstract”. Your solution only checks the
presence of these methods at runtime, which is already checked
by Ruby itself.

I don’t want the just same as Java’s abstract method.
I know Ruby is dynamic language.
I don’t intend to bring checking at compiletime nor strictly typing.

Yukihiro M. wrote:

...

end

The abstract “each” method defined in Enumerable overrides the “each”
in the Base class. I’m not against the idea of abstract (or deferred)
method, but sometimes it’s not that simple.

Oh, I didn’t realized that.
How about this?

class Derived < Base
alias each_orig each
include Enumerable
alias each each_orig

end

It’s not simple…orz

removed_email_address@domain.invalid wrote:

class Module
def abstract_method m
define_method(m){|*a| super rescue raise NotImplementedError}
end
end

It seems to be a good solution.
I’ll consider and examine this idea.
Thanks ara.