[RCR] abstract method in Ruby

K

kwatch

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

kwatch

Thanks Austin,

Austin said:
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
 
L

Logan Capaldo

Thanks Austin,



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



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



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.

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

Jacob Fugal

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'."
----------
class Module
def abstract_method(*method_names)
method_names.each do |name|
s =3D <<-END
def #{name}
mesg =3D "\#{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 =3D Foo.new
obj.m1 #=3D> Foo#m1() is not implemented yet. (NotImplementedError)

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 =3D "#{self} requires that the host implement the
method '%s'."
method_names.each do |name|
mesg =3D 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 =3D MyClass.new
b =3D 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:in `foo'
from abstract.rb:37
 
K

kwatch

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

kwatch

Jacob said:
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


Jacob said:
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'."
----------
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)

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:in `foo'
from abstract.rb:37
 
M

Meinrad Recheis

------=_Part_6075_13950129.1142151951941
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

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

------=_Part_6075_13950129.1142151951941--
 
L

Logan Capaldo

--Apple-Mail-1-661157955
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


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 :)
(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." :)
--Apple-Mail-1-661157955--
 
E

Erik Veenstra

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/
 
B

Benjohn Barnes

On 12 Mar 2006, at 20:48, Austin Ziegler wrote:
*snip*
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.

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.

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

Yukihiro Matsumoto

Hi,

In message "Re: [RCR] abstract method in Ruby"

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

ara.t.howard

Hi,

In message "Re: [RCR] abstract method in Ruby"

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

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
 
M

Mauricio Fernandez

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

ara.t.howard

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

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
 
K

kwatch

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

kwatch

Logan said:
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 :)
(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.
 
K

kwatch

Erik said:
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 said:
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.

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


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

ara.t.howard

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.

careful - ruby is strongly typed. what it is not is statically typed.

ruby typing => strong and dynamic

just wanted to point this out because the issue really seems to confuse
people.


i'm all for your RCR btw...


regards.


-a
 
A

ara.t.howard

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

sure. this one's a bit more robust since it doesn't mask errors:

harp:~ > cat a.rb
class Module
def abstract_method m
define_method(m) do |*a|
begin; super; rescue NoMethodError; raise NotImplementedError, m; end
end
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"


regards.

-a
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: [RCR] abstract method in Ruby"

|> 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.
|
|Oh, I didn't realized that.

It's caused by mix-in, a kind of multiple inheritance. The above is
rather artificial case, so that it does not cause troubles on most of
the cases, but if we want to add abstract method in the core, I think
we need to care about such cases as well.

matz.
 

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,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top