Forum: Ruby abstract method in Ruby

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-11 13:40
(Received via mailing list)
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?
31ab75f7ddda241830659630746cdd3a?d=identicon&s=25 Austin Ziegler (Guest)
on 2006-03-11 16:03
(Received via mailing list)
On 3/11/06, kwatch <kwa@kuwata-lab.com> 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
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-11 23:24
(Received via mailing list)
Thanks Austin,

Austin Ziegler 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.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-12 00:01
(Received via mailing list)
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.
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 Benjohn Barnes (Guest)
on 2006-03-12 00:16
(Received via mailing list)
On 11 Mar 2006, at 22:58, Logan Capaldo 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.

:) 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... :) I think I like the
idea of a declaration for an abstract methods because it makes clear
your intention.
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2006-03-12 00:55
(Received via mailing list)
On 3/11/06, kwatch <kwa@kuwata-lab.com> 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:in `foo'
          from abstract.rb:37
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-12 08:39
(Received via mailing list)
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
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-12 09:15
(Received via mailing list)
Jacob Fugal 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
48d1aca7191f2d16e184971054c7c143?d=identicon&s=25 Meinrad Recheis (Guest)
on 2006-03-12 09:27
(Received via mailing list)
On 3/12/06, kwatch <kwa@kuwata-lab.com> 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
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-12 16:27
(Received via mailing list)
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 :)
(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." :)
2e4e064abe3ea2a2ccc5b64acdc1ab08?d=identicon&s=25 Erik Veenstra (Guest)
on 2006-03-12 21:26
(Received via mailing list)
> 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/
31ab75f7ddda241830659630746cdd3a?d=identicon&s=25 Austin Ziegler (Guest)
on 2006-03-12 21:51
(Received via mailing list)
On 3/12/06, kwatch <kwa@kuwata-lab.com> 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
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 Benjohn Barnes (Guest)
on 2006-03-13 00:03
(Received via mailing list)
On 12 Mar 2006, at 20:48, Austin Ziegler 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.

:) 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.
0ec4920185b657a03edf01fff96b4e9b?d=identicon&s=25 Yukihiro Matsumoto (Guest)
on 2006-03-13 00:06
(Received via mailing list)
Hi,

In message "Re: [RCR] abstract method in Ruby"
    on Sun, 12 Mar 2006 16:38:44 +0900, "kwatch" <kwa@kuwata-lab.com>
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.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 01:18
(Received via mailing list)
On Mon, 13 Mar 2006, Yukihiro Matsumoto 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
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 Mauricio Fernandez (Guest)
on 2006-03-13 01:27
(Received via mailing list)
On Mon, Mar 13, 2006 at 09:16:30AM +0900, ara.t.howard@noaa.gov 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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 01:55
(Received via mailing list)
On Mon, 13 Mar 2006, Mauricio Fernandez 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
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-13 02:40
(Received via mailing list)
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.
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-13 04:10
(Received via mailing list)
Logan Capaldo 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 :)
> (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.
8e1efd5009e8efa38aa4393bc223f3c5?d=identicon&s=25 kwatch (Guest)
on 2006-03-13 04:35
(Received via mailing list)
Erik Veenstra 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 Matsumoto 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


ara.t.how...@noaa.gov 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.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 04:41
(Received via mailing list)
On Mon, 13 Mar 2006, kwatch wrote:

> 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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 05:11
(Received via mailing list)
On Mon, 13 Mar 2006, kwatch wrote:

> ara.t.how...@noaa.gov 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.

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
0ec4920185b657a03edf01fff96b4e9b?d=identicon&s=25 Yukihiro Matsumoto (Guest)
on 2006-03-13 06:48
(Received via mailing list)
Hi,

In message "Re: [RCR] abstract method in Ruby"
    on Mon, 13 Mar 2006 12:33:45 +0900, "kwatch" <kwa@kuwata-lab.com>
writes:

|>   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.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-13 06:55
(Received via mailing list)
On Mar 12, 2006, at 10:08 PM, kwatch wrote:

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

I don't believe it is any more descriptive than the current
situation. I think that whenever possible, you are right code should
be self-describing, but I don't think abstract methods are self-
describing.

I think that in ruby this already _is_ self describing:

module Enumerable
   ...
   def map(&block)
         results = []
         each { |x| results << block.call(x) }
          results
   end
end

Looking at this a programmer using this api will say, ok I need to
provide an each method,
and it needs to take a block. That's what the code tells him. If he
doesn't read the code, and attempts to use Enumerable, he will get an
exception. This exception will tell him what method his object
doesn't implement that enumerable relies upon. This exception may be
a NoMethodError, or a NotImplementedError. Either way though he has
to go back to the code or docs to see what is expected. Implementing
an each that doesn't take a block or an each that takes more or less
arguments than its expected too is just as bad as or worse than no
each at all. I digress though, because what it really comes down to
is that your abstract_method should be a comment or an rdoc
directive. It really, really should. There is no reason to write code
that does effectively nothing. Your abstract method (irrespective of
abstract methods in other languages) is a documentation tool, and
that's where it should be implemented. You say "consider
readability", so do I. What is more readable, a comment telling me I
need to have an each method to include Enumerable, possibly even
explaining why, or a a call to a class method named abstract _method?
Now admittedly I can guess what that means, but I still have to find
out to be sure. Especially since it looks like its doing something. A
comment can be just as short as your method call, doesn't introduce
any new code and it IS part of the code, unless you read your code
with all the comments stripped out. You keep saying code should be
descriptive, I don't believe this adds to the descriptiveness. Which
is why I would not want this in the core ruby. It doesn't add anything.
F1d6cc2b735bfd82c8773172da2aeab9?d=identicon&s=25 unknown (Guest)
on 2006-03-13 13:54
(Received via mailing list)
Hi,

At Mon, 13 Mar 2006 13:10:45 +0900,
ara.t.howard@noaa.gov wrote in [ruby-talk:183841]:
>      harp:~ > cat a.rb
>      class Module
>        def abstract_method m
>          define_method(m) do |*a|
>            begin; super; rescue NoMethodError; raise NotImplementedError, m; end

This hides NoMethodError within the super method too.

Instead:

      defined?(super) or raise NotImplementedError, m
      super
2e4e064abe3ea2a2ccc5b64acdc1ab08?d=identicon&s=25 Erik Veenstra (Guest)
on 2006-03-13 15:36
(Received via mailing list)
> 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.

In this case (class definitions in Ruby), the closest to
"compile time" is probably "allocate time". I mean, the first
time you use a class after you defined it, is the moment you
create an instance of that class. (Other ideas?)

If you want to check a class for specific methods, "allocate
time" might be the right time to do so. (Unless you're willing
to wait until you run into the traditional NoMethodError.) I
came up with the following, using wrap_module_method (as
discussed before [1] and summarized here [2]) to wrap
Class#new.

This is probably not a good solution, unless you don't care
about performance... It might be optimized by unwrapping the
Class#new once the class has been checked. Although that's just
a theory, for now...

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

 [1] http://tinyurl.com/ntbl4
 [2] http://www.erikveen.dds.nl/monitorfunctions/index.html

----------------------------------------------------------------

 require "ev/metameta"	# That's where the code from [2] lives...

 class Class
   def abstract_method(*method_names)
     wrap_module_method(:new) do |org_method, args, block, obj|
       method_names.each do |method_name|
         obj.instance_eval do
           unless instance_methods.include?(method_name.to_s)
             raise NotImplementedError, "Class #{self} doesn't
implement method #{method_name}."
           end
         end
       end

       org_method.call(*args, &block)
     end
   end
 end

----------------------------------------------------------------

 class Foo
   abstract_method :baz
 end

 class Bar1 < Foo
 end

 class Bar2 < Foo
   def baz
     :ok
   end
 end

 [Bar1, Bar2].each do |klass|
   begin
     klass.new
   rescue NotImplementedError => e
     $stderr.puts e.message
   end
 end
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 17:02
(Received via mailing list)
On Mon, 13 Mar 2006 nobu@ruby-lang.org wrote:

> This hides NoMethodError within the super method too.
>
> Instead:
>
>      defined?(super) or raise NotImplementedError, m
>      super

hmmm - but that (both actually) fails here:

     harp:~ > cat a.rb
     class Module
       def abstract_method m
         define_method(m) do |*a|
           defined?(super) ? super : raise(NotImplementedError, m)
         end
       end
     end

     class A
       def foo; 42; end
     end
     class B < A
       abstract_method "foo"
     end

     p B::new.foo


     harp:~ > ruby a.rb
     42


what about this fix?


     harp:~ > cat a.rb
     class Module
       def abstract_method m
         already_respond_to_m = instance_method(m) rescue nil

         define_method(m) do |*a|
           if already_respond_to_m
             raise(NotImplementedError, m)
           else
             defined?(super) ? super : raise(NotImplementedError, m)
           end
         end

       end
     end

     class A
       def foo; 42; end
     end
     class B < A
       abstract_method "foo"
     end

     p B::new.foo


     harp:~ > ruby a.rb
     b.rb:7:in `foo': foo (NotImplementedError)
             from b.rb:23


thoughts?

-a
391f9b787cdc12aa2c179713f5103e3a?d=identicon&s=25 Ilan Berci (iberci)
on 2006-03-13 17:04
A good portion of the GoF patterns rely on abstract methods (visitor,
abstract factory, template method, etc... ) which IMO only shine in
statically typed languages.  It would be a shame to port all patterns
(and their required prerequisite: abstract methods) simply because they
are recognized in the static world and therefore would make a good fit
in Ruby.

The additional code needed for the ruby "abstract method" approach
doesn't make the code any more readable.  The extra LoC actually slows
down the maintenance developer as she has to wade through more.

In both solutions, an exception is thrown at runtime.  The abstract
method approach will not allow the developer to catch it any earlier
than the duck typed default.

The exception proposed although slightly more readable will not in
practice add any value as Ruby developers are used to seeing the
exception and can narrow in on it expediently.

We should simply rejoice in the power of the mixin and realize that the
abstract specifier was devised prior to (and probably due to the absence
of) this incredibly powerfull construct.

Lastly, the code becomes somewhat heavier which violates 2 key
principles of Ruby, one being the enjoyable to code principle, and the
other, least surprise principle.

ilan
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 17:05
(Received via mailing list)
On Mon, 13 Mar 2006, Erik Veenstra wrote:

> In this case (class definitions in Ruby), the closest to "compile time" is
> probably "allocate time". I mean, the first time you use a class after you
> defined it, is the moment you create an instance of that class. (Other
> ideas?)

how interesting!

this is a very appropriate for some circumstances which i'd never
thought of -
i'm sure i'll steal it in the near future.  ;-)

regards.

-a
2e4e064abe3ea2a2ccc5b64acdc1ab08?d=identicon&s=25 Erik Veenstra (Guest)
on 2006-03-13 17:54
(Received via mailing list)
> The exception proposed although slightly more readable will
> not in practice add any value as Ruby developers are used to
> seeing the exception and can narrow in on it expediently.

I'm not advocating these abstract methods in Ruby. On the
contrary, I don't like them either.

It's just that, when I read this thread, I thought: Can I do
that with my meta-meta-programming library? Answer: yes. That's
all what I wanted to know... :)

It's not about "yes or no", but about "how if yes"...

gegroet,
Erik V. - http://www.erikveen.dds.nl/
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 18:00
(Received via mailing list)
On Tue, 14 Mar 2006, ilan berci wrote:


> A good portion of the GoF patterns rely on abstract methods (visitor,
> abstract factory, template method, etc... ) which IMO only shine in
> statically typed languages.  It would be a shame to port all patterns (and
> their required prerequisite: abstract methods) simply because they are
> recognized in the static world and therefore would make a good fit in Ruby.
>
> The additional code needed for the ruby "abstract method" approach doesn't
> make the code any more readable.  The extra LoC actually slows down the
> maintenance developer as she has to wade through more.

i disagree strongly here.  check out these two snippets that i am
working on
as we speak:

     def will_process_holding *a, &b
       raise NotImplementedError
     end
     def process_holding *a, &b
       raise NotImplementedError
     end
     def will_process_incoming(*a, &b)
       raise NotImplementedError
     end
     def process_incoming(*a, &b)
       raise NotImplementedError
     end

vs

     abstract_method "will_process_holding"
     abstract_method "process_holding"
     abstract_method "will_process_incoming"
     abstract_method "process_incoming"


it's for precisely these kinds of simple repetetive defs that we have
'attr',
'attr_accessor', 'attr_writer', and 'attr_reader'.  think about how
simple all
these methods are to write - but people love the shorthand and the fact
that
rdoc can grok something meaningful from the metaprogramming is quite
beneficial.

sure - i could just go full on duck type - but good luck reading the
2000 line
satellite image processing library that requires these four methods (and
about
20 others) and trying to figure out which ones you need to implement.  i
nice
compact listing like this surely helps out with that task doesn't it?

here's another example from the standard lib

     tsort.rb
     ...
       #
       # Should be implemented by a extended class.
       #
       # #tsort_each_node is used to iterate for all nodes over a graph.
       #
       def tsort_each_node # :yields: node
         raise NotImplementedError.new
       end

       #
       # Should be implemented by a extended class.
       #
       # #tsort_each_child is used to iterate for child nodes of _node_.
       #
       def tsort_each_child(node) # :yields: child
         raise NotImplementedError.new
       end
     ...

now, all this starts out at about line 230.  this is a little deep is it
not?
i think it'd be great of rdoc understood and abstract_method hook just
like it
now supports the 'attr_XXX' hooks but, even without that, i'll take any
hint i
can get in code like this.

ask yourself this: why did the author not just rely on duck-typing?  why
put
these methods into the code at all?

> In both solutions, an exception is thrown at runtime.  The abstract method
> approach will not allow the developer to catch it any earlier than the duck
> typed default.

unless, using one approach it takes an hour to figure out which methods
to
impliment and the other way it does not.  consider an abstract method

   module Interface
     def extra_debugging_info e
       ...
     end
   end

used like

   module Interface
     def foobar
       begin
         ...
         ...
       rescue ExceptionThatOccursOncePerMonth => e
         logger.warn{ extra_debugging_info e}
       end
     end
   end


how anoying!  you really should have written that method because your
code now
fails once per month.  having an 'abstract_method' hook makes it so,
even if
the developer didn't comment their code, the code itself clearly showed
the
list of methods which should be implimented.

trust me - i realize this is really a small gain over pure duck typing -
but
for the kinds of systems i'm writing, with 10,000 lines of ruby code,
it's
really quite helpful to have this sort of note to myself jump out in the
code.  that's why i've already written this:

   class Subscription
...
     def self::abstract_method m
       module_eval <<-definition
         def #{ m }(*a, &b)
           raise NotImplementedError
         end
       definition
     end
...


> The exception proposed although slightly more readable will not in
> practice add any value as Ruby developers are used to seeing the
> exception and can narrow in on it expediently.

agreed.  the point is to not get the exception in the first place
becausee the
code was easier to understand how to hook into.

> We should simply rejoice in the power of the mixin and realize that the
> abstract specifier was devised prior to (and probably due to the absence
> of) this incredibly powerfull construct.
>
> Lastly, the code becomes somewhat heavier which violates 2 key
> principles of Ruby, one being the enjoyable to code principle, and the
> other, least surprise principle.

i really don't see why people would object to this idea.  is it because
people
are only writing 100 line programs in ruby that might require one
abstract
method?  i just don't understand how someone could think that diving
into a
complex inheritence hierarchy of 30 classes/modules requiring 20 or so
abstract
methods would NOT be made easier to grok if one could grep for
/abstract_method/ in their editor to get a jump on things...

my 2cts.

kind regards.

-a
31ab75f7ddda241830659630746cdd3a?d=identicon&s=25 Austin Ziegler (Guest)
on 2006-03-13 19:39
(Received via mailing list)
On 3/13/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>> slows down the maintenance developer as she has to wade through more.
>        raise NotImplementedError
>      abstract_method "process_incoming"
Okay, Ara. Why do these methods need to be abstract and specifically
have code around them? IMO, the *same* question should be asked of
tsort.rb. I think that, in both cases, the answer is the same: they
don't.

Adding a definition for an "abstract_method" really doesn't add
*anything* to the readability of the code that a well-written comment
(which *is* read by rdoc without modification) doesn't provide.

[...]

> sure - i could just go full on duck type - but good luck reading the
> 2000 line satellite image processing library that requires these four
> methods (and about 20 others) and trying to figure out which ones you
> need to implement.  i nice compact listing like this surely helps out
> with that task doesn't it?

I'm not sure. As I said earlier, PDF::Writer doesn't use them. In
development versions of PDF::Writer, things like that have appeared --
and promptly been removed because they add clutter, not clarity.

[...]
> now, all this starts out at about line 230.  this is a little deep is
> it not? i think it'd be great of rdoc understood and abstract_method
> hook just like it now supports the 'attr_XXX' hooks but, even without
> that, i'll take any hint i can get in code like this.

> ask yourself this: why did the author not just rely on duck-typing?
> why put these methods into the code at all?

I ask, instead, how old tsort.rb is -- and that will generally indicate
why it has defined abstract methods.

>> In both solutions, an exception is thrown at runtime.  The abstract
>> method approach will not allow the developer to catch it any earlier
>> than the duck typed default.
> unless, using one approach it takes an hour to figure out which
> methods to impliment and the other way it does not.

Ara, this ignores the examples I gave early on:

  # All clients of this module must implement #each with no parameters.
  module Enum
    def map
      arr = []
      each { |elem| arr << yield(elem) }
      arr
    end
  end

I have just made #each an abstract method without adding any code at
all.

>> The exception proposed although slightly more readable will not in
>> practice add any value as Ruby developers are used to seeing the
>> exception and can narrow in on it expediently.
> agreed.  the point is to not get the exception in the first place
> becausee the code was easier to understand how to hook into.

And I disagree that it makes it easier to read, Ara. I don't write
10,000 line programs, but PDF::Writer is 5,000 lines. Certain items,
especially in current development versions, could *easily* have been
declared as "abstract" (and would have to had been in static languages)
but aren't necessary to do such in Ruby.

> i really don't see why people would object to this idea.  is it
> because people are only writing 100 line programs in ruby that might
> require one abstract method?  i just don't understand how someone
> could think that diving into a complex inheritence hierarchy of 30
> classes/modules requiring 20 or so abstract methods would NOT be made
> easier to grok if one could grep for /abstract_method/ in their editor
> to get a jump on things...

I really don't think that there's need for this in the core. I think
that the use of it is a crutch; with the programs you have to write,
Ara, that might be necessary. (I've used similar crutches in some
versions of PDF::Writer experiments.)

-austin
391f9b787cdc12aa2c179713f5103e3a?d=identicon&s=25 Ilan Berci (iberci)
on 2006-03-13 20:01
I still prefer the "full on type ducking" with a readable comment in the
file. As for the exception that occurs once a month, I get the same
thing in statically compiled language!  Sure I am guaranteed that the
method exists due to my compiler bulking at the ommission of my method
definition but since it will only get exercized once a month, I probably
won't see the error till the runtime hits it anyway.  (and yes, I have
had yearly bugs pop up on new years day..)  The answer is proper test
cases that detect these things early.  A compiler making sure your
method exists is not a proper test of the monthly condition that may
occur.

As an aside, I should state that all my Ruby is on peripheral projects
assisting my main enterprise development project. The complexity of my
Ruby projects has not reached 10k LoC so I should make that caveat
clear. :)

ilan

unknown wrote:

>
> i disagree strongly here.  check out these two snippets that i am
> working on
> as we speak:
>
>      def will_process_holding *a, &b
>        raise NotImplementedError
>      end
>      def process_holding *a, &b
>        raise NotImplementedError
>      end
>      def will_process_incoming(*a, &b)
>        raise NotImplementedError
>      end
>      def process_incoming(*a, &b)
>        raise NotImplementedError
>      end
>
> vs
>
>      abstract_method "will_process_holding"
>      abstract_method "process_holding"
>      abstract_method "will_process_incoming"
>      abstract_method "process_incoming"
>
>
Fea3202718832fe11a0db6bb44a5cc37?d=identicon&s=25 Avdi Grimm (Guest)
on 2006-03-13 20:03
(Received via mailing list)
On 3/13/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

>        raise NotImplementedError
>      abstract_method "process_incoming"
The question is, why use either of those methods?  Why not just note
in your documentation that those four functions must be defined?  The
user is going to have to read the documentation anyway, in order to
understand what those four abstract methods are for.  A
"NoMethodError" conveys the same information as a
"NotImplementedError", in this case.

And this is arguably an abuse of NotImplementedError.  The usual
semantics of NotImplementedError are to indicate that the method is
either a) not yet implemented by a library; or b) is not supported on
the current platform.  Whereas in the example above it is being used
to indicate that a precondition was not met.

I see that you're trying to make your code more declarative and more
explicit about it's semantics.  But seeing 'abstract_method
:will_process_holding' doesn't tell me anything more than getting a
"NoMethodError: will_process_holding".  Both of them just tell me I'm
going to have to go look at the documentation to learn what the heck
#will_process_holding is supposed to do.

If you really want to add useful semantic information, then put in
more specific error information where the "abstract" method is
*called*.  E.g.:

    def frobnicate(duck)
        duck.quack() rescue NoMethodError raise("Ducks must quack in
order to be frobnicated.  See documentation.")
    end

If you want to get fancy and declarative and proactive about it, you
could make a #precondition method:

    def frobnicate(duck)
        precondition("Ducks must quack in order to be frobnicated"){
duck.respond_to? :quack}
    ...
    end

I believe there are some libraries out there that assist in doing this
kind of "strict duck typing".

As a bonus, you are keeping your preconditions close to the code that
actually depends on them, which reduces the chances that they will get
out of sync with your code, and give the user false information.

~Avdi
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-13 20:21
(Received via mailing list)
On Tue, 14 Mar 2006, Austin Ziegler wrote:

> Okay, Ara. Why do these methods need to be abstract and specifically have
> code around them? IMO, the *same* question should be asked of tsort.rb. I
> think that, in both cases, the answer is the same: they don't.
>
> Adding a definition for an "abstract_method" really doesn't add *anything*
> to the readability of the code that a well-written comment (which *is* read
> by rdoc without modification) doesn't provide.

you are perfectly correct.  however, i think we all know that there is a
lot
of code, especially in-house, where the docs are pretty thin.  in my
case
keeping 30,000 lines of ruby documented makes no sense since

   - i'm the only developer

   - we do research and the code changes daily

   - it's less work to use long variable names and abstract code at
every
     possible opportunity into stand-alone libs than to write docs.

it just makes my life easier to do things like (all real examples)

   native_int_attribute 'samples'

   xdr_long_attribute 'grid_size'

   $LOAD_PATH << "nrtlib" # vs $:

   abort "#{ $PROGRAM_NAME } parse failure" # vs $0

   hash_of_subscriber_to_prefixes = {}

   abstract_method 'foo'

   required_argument 'dirname'

   option '--verbose', '-v'

in otherwords, to make the code slightly more verbose, self describing,
editor
searchable (this is huge ihmho), and clear to someone new taking over
the
project because there aint' any docs.

also, i'd maintain encoding sematics into docs is more difficult that it
sounds.  if it weren't the entire stdlib would have great docs - lord
knows
there has been enough time and man power available.  but the truth is in
the
puddin - people (myself included) don't do it (enough).

> I'm not sure. As I said earlier, PDF::Writer doesn't use them. In
> development versions of PDF::Writer, things like that have appeared -- and
> promptly been removed because they add clutter, not clarity.

trust me - i with you.  sometimes.  most of my libs take that approach
too.
but sometimes it's nice to be extra clear.

the thing is, having 'abstract_method' does not prevent one from using
your
approach - it's simply another arrow in the quiver.

> I ask, instead, how old tsort.rb is -- and that will generally indicate why
> it has defined abstract methods.

sure.  but it's in the stdlib as is alot of old code where often the
code is
the only correct/detailed documentation.  i don't even use ri or rdoc
anymore
- i prefer to simply do something like

   vim -o webrick.rb webrick/*

and peek around.  it would be great to then do

   /abstract_method

just as i know often do

   /attr

or

   /def <<

etc.

>
> I have just made #each an abstract method without adding any code at
> all.

oh i know, and i'd never use 'abstract_method' here - but what if the
number
of 'virtual abstract methods' exceeded fifty?

> And I disagree that it makes it easier to read, Ara. I don't write
> 10,000 line programs,

(OT: me neither btw - but all the libs/progs together approach 30k in my
latest system - i think the largest __file__ is only about 1000 lines.
fyi)

> but PDF::Writer is 5,000 lines. Certain items, especially in current
> development versions, could *easily* have been declared as "abstract" (and
> would have to had been in static languages) but aren't necessary to do such
> in Ruby.

> I really don't think that there's need for this in the core. I think
> that the use of it is a crutch; with the programs you have to write,
> Ara, that might be necessary. (I've used similar crutches in some
> versions of PDF::Writer experiments.)

you may be right here - perhaps a lib only.  still, one could make
almost
every argument against 'attr' and co. you've made against
'abstract_method'.
both are unnecessary simple convenience solutions for simple problems.
still,
unless a more grandiose vision of abstract methods/interfaces is in the
plans
- what would it hurt?

kind regards.

-a
93d566cc26b230c553c197c4cd8ac6e4?d=identicon&s=25 Pit Capitain (Guest)
on 2006-03-13 21:01
(Received via mailing list)
ara.t.howard@noaa.gov schrieb:
> (... implementing Module#abstract_method ...)

All those implementations which actually create dummy methods prohibit
implementing an abstract method using method_missing.

Regards,
Pit
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-13 21:33
(Received via mailing list)
On Mar 13, 2006, at 2:21 PM, ara.t.howard@noaa.gov wrote:

>   - it's less work to use long variable names and abstract code at
> every
>     possible opportunity into stand-alone libs than to write docs.

What's the big difference between

# abstract method foo

and

abstract_method :foo ?

This is what's boggling my mind. It's not writing some documentation,
its writing it in the same place you'd call your abstract_method
method anyway. And you can still search for it within vim or
whatever. I don't know enough about your particular case for
"native_int_atrribute" but tyou're probably talkign to some library
that expects an int and that method probably does some checking on
assignment, which is fine. That makes sense. What I'm suggesting is
that although a program should explode immediately when there's a
problem, it also shouldn't explode when there isn't one. If I never
call map, each will never be called, so its ok if I don't implement
it. Then on the day I do call it, and run it for the first time, it
expldoes right there, when I'm calling each. I know immediately what
change I _just_ made caused it to explode. OTOH if I mixin Enumerable
w/o an each, and each is an "abstract_method" it breaks whether I use
it or not. But when everbody starts using abstract methods, all of a
sudden I'm back in the land of compile-fix, compile-fix. Cause that
error is gonna happen right away, so I have to deal with that
immediate error. Then I can run it again to make sure it works, but
now the error happens again, compile-fix ad infinitum.
912c61d9da47754de7039f4271334a9f?d=identicon&s=25 unknown (Guest)
on 2006-03-13 22:30
(Received via mailing list)
Quoting ara.t.howard@noaa.gov:

> sure.  this one's a bit more robust since it doesn't mask errors:
>
>        def abstract_method m
>          define_method(m) do |*a|
>            begin; super; rescue NoMethodError; raise
> NotImplementedError, m; end
>          end
>        end

Hmm, what about cases where the super exists, but calls something
which itself raises a NoMethodError?

-mental
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2006-03-13 23:38
(Received via mailing list)
On 3/13/06, Logan Capaldo <logancapaldo@gmail.com> wrote:
> error is gonna happen right away, so I have to deal with that
> immediate error. Then I can run it again to make sure it works, but
> now the error happens again, compile-fix ad infinitum.

Umm, except, no. Maybe if we used Erik's "allocation-time checking"
approach, but not with kwatch's NotImplementedError approach. If you
mixin Enumerable, but then never call map/inject/collect/etc., you'll
never get the NotImplementedError exception, just as you never got the
NoMethodError exception. As far as *behavior*, timing/severity of the
failure is exactly the same -- you get an exception when the library
tries to use the method you should have defined.

The value of Erik's approach (which, as Pit and Matz pointed out,
needs refining) is in self-documentation and explicit error messages.
Obviously, you and Ara don't agree on how much that self-documentation
is worth. I'm not sure either; you both make good arguments. This is
the reason why I also vote against it being in the core.

However, I *highly* value the more descriptive exception. I'll take an
exception that tells me "If you want to extend Enumerable, you need to
provide an 'each' method" over "NoMethodError: each" any day. Sure, I
have to go back to the documentation and see what that each method
should do, but I know more precisely why things are failing. You may
disagree, and that's fine. I'm just staing my opinion as to why I
think it will be a valuable non-core library.

Jacob Fugal
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-03-14 02:38
(Received via mailing list)
ara.t.howard@noaa.gov wrote:
...
> i think it'd be great of rdoc understood and abstract_method hook
> just like it now supports the 'attr_XXX' hooks but, even without
> that, i'll take any hint i can get in code like this.

Rdoc does! Just use the -A option:

rdoc -A abstract_method=ABSTRACT

or something like that. Change ABSTRACT to be something you like that
will tag the attr the the rdoc output.

rdoc -h | grep -A 5 "accessor"
       --accessor, -A accessorname[,..]
                 comma separated list of additional class methods
                 that should be treated like 'attr_reader' and
                 friends. Option may be repeated. Each accessorname
                 may have '=text' appended, in which case that text
                 appears where the r/w/rw appears for normal accessors.
...
F1d6cc2b735bfd82c8773172da2aeab9?d=identicon&s=25 unknown (Guest)
on 2006-03-14 05:38
(Received via mailing list)
Hi,

At Tue, 14 Mar 2006 01:01:31 +0900,
ara.t.howard@noaa.gov wrote in [ruby-talk:183918]:
>
>      harp:~ > ruby a.rb
>      42

Simply separate Module#abstract_method and Class#abstract_method.

     class Class
       def abstract_method m
         define_method(m) do |*a|
           raise(NotImplementedError, m)
         end
       end
     end
Afb2adb7cc2540207e96ff9ad1b5cb96?d=identicon&s=25 Bill Barnhill (Guest)
on 2006-03-14 15:01
(Received via mailing list)
Hmm, just some thoughts.
I think the abstract method idea nice in concept, but implementation
perhaps
too Java-ish.

If I get a chance later this week I'll code this, but for now here's how
I'd
envision something like this within Ruby code:

class A
    should_respond_to_behavior  #..options hash describing what to do if
msg
not
                                               # responded to properly
goes
here,
                                               # nice to support YAML as
well..
    should_respond_to :foo, :bar => {}, :baz => {:param_count => 3}
end

class B < A
    def initialize
       @a = "foo"
end

B.new
=begin
At this point the class a checks responses:
.. it expects a response to messages :foo and :bar with any or no params
.. it expects a response to baz with three params
.. raises exception if either check fails, or logs, depending on
configuraiton

You could extend the above by having behavior contrained to
normative language...
.. should_respond_to  just logs warning if msg not responded to properly
.. must_respond_to  raises exception if msg not responded to properly

Even the following, though HTBD (Here there be dragons..i.e. I'm a
little
quesy about this last one)
.. may_respond_to
Same syntax as above, but means that any instances can only respond to
the
defined messages. I can see several uses for this, but also see several
misuses and implementing so you could be reasonably sure about
constraint
would be a pain, and might have to be in Ruby internals. This behavior
also
doesn't match normative language use, perhaps a
.. must_only_respond_to


--Bill

Bill Barnhill
i-name: xri://=Bill.Barnhill (For more info:
http://2idi.com/grs/index.php?referral_code=communitivity )
Afb2adb7cc2540207e96ff9ad1b5cb96?d=identicon&s=25 Bill Barnhill (Guest)
on 2006-03-14 15:04
(Received via mailing list)
Oops, my apologies, forgot an end in example.
Should read
class B < A
    def initialize
       @a = "foo"
    end
end

Another though on this is that it would allow us to define message
interfaces (aka duck-typing prototypes).

--Bill

Bill Barnhill
i-name: xri://=Bill.Barnhill (For more info:
http://2idi.com/grs/index.php?referral_code=communitivity
)
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-03-14 15:26
(Received via mailing list)
Hi --

On Tue, 14 Mar 2006, Bill Barnhill wrote:

> Oops, my apologies, forgot an end in example.
> Should read
> class B < A
>    def initialize
>       @a = "foo"
>    end
> end
>
> Another though on this is that it would allow us to define message
> interfaces (aka duck-typing prototypes).

I fear that  "duck-typing prototype" is a contradiction in terms.
With duck typing, you're dealing with an object: it's all about the
moment you send the message to the object, and the *absence* of
concern about where that object came from, what its pedigree is, and
so forth.  "Message interfaces" suggests a higher-level clustering of
behaviors.

>>     should_respond_to_behavior  #..options hash describing what to do if
>>        @a = "foo"
>> end
>>
>> B.new
>> =begin
>> At this point the class a checks responses:
>> .. it expects a response to messages :foo and :bar with any or no params
>> .. it expects a response to baz with three params
>> .. raises exception if either check fails, or logs, depending on
>> configuraiton

Having a class police the behavior of its instances after they've
already been created strikes me as being at odds with some of the
basic conditions of Ruby runtime -- namely, that objects can change
and aren't constrained by the circumstances of their creation.  I
suspect it would also be likely to discourage duck typing.


David

--
David A. Black (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
Afb2adb7cc2540207e96ff9ad1b5cb96?d=identicon&s=25 Bill Barnhill (Guest)
on 2006-03-14 15:56
(Received via mailing list)
Good points both, thanks.

If I have the constraint that objects within a certain group must be
able to
respond to the methods that make up behavior X, then what would be the
best
way to ensure that?

If my group is represented by a collection then I can check for msg
responding on adding to the collection, but if I have a large number of
methods to check, and several different collections, then I'd rather not
have respond to code in each collection's insertion method.

If I declare a delegate at beginning of class with required methods
defined,
then as I def those methods later in class the delegate methods will get
replaced, right? Is that then the Ruby way to do this?

The behavior I am aiming for is essentially a 'kind_of?' for duck
typing,
which as you pointed out is mixing two opposite philosophies.  But I can
see
usefulness of something like pattern_of?..ahh, maybe a light bulb is
going
off here. Would this then be the ruby way:
.. patterned_obj.pattern_of?(pattern_obj) that does the following
  .. Gets symbols for public methods on pattern_obj
  .. Check to see that patterned_obj responds to all these methods

Still doesn't check for param count though, but it sounds like that may
be a
good thing, as Ruby makes nice use of different param types and
counts..example being Hash.each.

--Bill

Bill Barnhill
i-name: xri://=Bill.Barnhill (For more info:
http://2idi.com/grs/index.php?referral_code=communitivity
)
Afb2adb7cc2540207e96ff9ad1b5cb96?d=identicon&s=25 Bill Barnhill (Guest)
on 2006-03-14 15:59
(Received via mailing list)
My apologies for double posting, I must have confused  Gmail somehow.
Fea3202718832fe11a0db6bb44a5cc37?d=identicon&s=25 Avdi Grimm (Guest)
on 2006-03-14 16:02
(Received via mailing list)
On 3/14/06, Bill Barnhill <xmlarchitect@gmail.com> wrote:
> If I have the constraint that objects within a certain group must be able to
> respond to the methods that make up behavior X, then what would be the best
> way to ensure that?

The point of duck typing is that your code automatically ensures it,
without any extra work on your part, by raising exceptions when the
methods aren't implemented.  The bonus you get for relying on your
code to do just-in-time type verification is that the "prototypes"
never get out of sync with the actual requirements of the code,
because the code IS the prototype.

~Avdi
10d4acbfdaccb4eee687a428ca00a5d8?d=identicon&s=25 Jim Weirich (weirich)
on 2006-03-14 16:06
I've been following the discussion on abstract method declaration with
some interest.  It seems to me that the ideal implementation should not
only throw an exception on unimplemented methods, but also must pass
Matz's example (implementation provided by base classes) and the
method_missing example.

With that in mind, I started to develope an implementation of
abstract_method in a test first way, where you write a test case and
then only implement enough code to make the test pass.  Then you write
another test case and only add enough code to make that test pass.
Repeat as needed.

With that in mind, I came up with 5 test cases:

-- BEGIN UNIT TESTS ------------------------------------------------
require 'test/unit'
require 'abstract_method'

class TestAbstractMethod < Test::Unit::TestCase

  # This is the basic use case for abstract methods where the abstract
  # method is declared in the parent class and redefined in the child
  # class.
  def test_can_override_abstract_method
    parent = Class.new {
      abstract_method :foo
    }
    child = Class.new(parent) {
      def foo
        :foo_result
      end
    }
    assert_equal :foo_result, child.new.foo
  end

  # Here we make sure that not implementing the abstract method in the
  # child class will cause an exception when the method is invoked on
  # the child object.  We don't particularly care what exception is
  # thrown, but the exception message must mention the missing method
  # by name.
  def test_unimplemented_abstract_method_throws_exception
    parent = Class.new {
      abstract_method :foo
    }
    child = Class.new(parent) {
    }
    begin
      child.new.foo
      fail "Oops"
    rescue Exception => ex
      assert_match /\bfoo\b/, ex.message
    end
  end

  # Now we make sure that our implementation passes Matz's example
  # where an abstract method in a mixin is actually implemented in the
  # base class.  We need to make sure that the mixin doesn't hide the
  # implemented behavior.
  def test_abstract_method_in_mixin_may_be_implemented_in_base_class
    abstract_mixin = Module.new {
      abstract_method :foo
    }
    parent = Class.new {
      def foo
        :foo_result
      end
    }
    child = Class.new(parent) {
      include abstract_mixin
    }
    assert_equal :foo_result, child.new.foo
  end

  # This is a similar scenario to the previous test case where we make
  # sure the abstract declaration doesn't interfer with implemented
  # behavior.  This time the implemented behavior is provided by the
  # method missing technique.
  def test_abstract_method_may_be_implemented_by_method_missing
    parent = Class.new {
      abstract_method :foo
    }
    child = Class.new(parent) {
      def method_missing(sym, *args, &block)
        :foo_result
      end
    }
    assert_equal :foo_result, child.new.foo
  end

  # Finally we want to ensure that +abstract_method+ can take multiple
  # method names, and that the method names may be either strings or
  # symbols.
  def test_abstract_method_may_take_multiple_string_or_symbol_arguments
    parent = Class.new {
      abstract_method :foo, "bar", "baz"
    }
    child = Class.new(parent) {
      def foo
        :foo_result
      end
      def bar
        :bar_result
      end
    }
    assert_equal :foo_result, child.new.foo
    assert_equal :bar_result, child.new.bar
    begin
      child.new.baz
      fail "Oops"
    rescue Exception => ex
      assert_match /\bbaz\b/, ex.message
    end
  end
end
-- END UNIT TESTS -------------------------------------------------

And here is the implementation that came of that exercise:

-- BEGIN ABSTRACT METHOD IMPLEMENTATION ---------------------------

class Module
  def abstract_method(*method_names)
  end
end

-- END ABSTRACT METHOD IMPLEMENTATION -----------------------------

--
-- Jim Weirich
Afb2adb7cc2540207e96ff9ad1b5cb96?d=identicon&s=25 Bill Barnhill (Guest)
on 2006-03-14 16:15
(Received via mailing list)
Ok, I think I see what you mean. I didn't have the right understanding
of
duck-typing and your explanation of how it works make a lot of sense,
though
coming from Java the idea makes my stomach a bit queasy as I relied on
Interfaces a lot.

Thanks for help,
--Bill
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-14 16:18
(Received via mailing list)
On Mar 14, 2006, at 9:07 AM, Jim Weirich wrote:

> And here is the implementation that came of that exercise:
>
> -- BEGIN ABSTRACT METHOD IMPLEMENTATION ---------------------------
>
> class Module
>   def abstract_method(*method_names)
>   end
> end
>
> -- END ABSTRACT METHOD IMPLEMENTATION -----------------------------

Priceless.

James Edward Gray II
93d566cc26b230c553c197c4cd8ac6e4?d=identicon&s=25 Pit Capitain (Guest)
on 2006-03-14 16:21
(Received via mailing list)
Jim Weirich schrieb:
> ...
> And here is the implementation that came of that exercise:
>
> class Module
>   def abstract_method(*method_names)
>   end
> end

LOL!!! Very nice! You should post this to the TDD mailing list.

Regards,
Pit
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 unknown (Guest)
on 2006-03-14 16:48
(Received via mailing list)
> I've been following the discussion on abstract method declaration with
> some interest.  It seems to me that the ideal implementation should not
> only throw an exception on unimplemented methods, but also must pass
> Matz's example (implementation provided by base classes) and the
> method_missing example.

:) I like your approach, and I think it's extremely close. I'd change
one of your tests a little though:

>   # Here we make sure that not implementing the abstract method in the
>   # child class will cause an exception when the method is invoked on
>   # the child object.  We don't particularly care what exception is
>   # thrown, but the exception message must mention the missing method
>   # by name.
>   def test_unimplemented_abstract_method_throws_exception
...

It would be preferable I think, if a more helpful error message were
given, instead of simply the method being missing.

And while I suspect you're being a little flipant, I like your
implementation - it does do nearly everything that's desired.
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-03-14 16:54
(Received via mailing list)
Hi --

On Wed, 15 Mar 2006, benjohn@fysh.org wrote:

>>   # child class will cause an exception when the method is invoked on
> implementation - it does do nearly everything that's desired.
I don't think Jim is being flippant at all.  He's provided a fantastic
illustration of something that's true generally about Ruby, namely
that it is incredibly expressive.  Many of the twists and turns one
takes in trying to get Ruby to do, or be, this or that end up adding
up to a slip-knot; and what you really have to do is something that
looks negligible.

I'm happy without abstract methods in Ruby but it's worth the lengthy
discussion to get to Jim's post :-)


David

--
David A. Black (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
10d4acbfdaccb4eee687a428ca00a5d8?d=identicon&s=25 Jim Weirich (weirich)
on 2006-03-14 16:58
unknown wrote:
> It would be preferable I think, if a more helpful error message were
> given, instead of simply the method being missing.

Agreed.  The unit test only specifies that the method name should be
mentioned.  I'm not sure how to test for a "helpful" error message
without specifying the exact text to be shown ... and I didn't want to
overconstrain the solution.

Perhaps Nathaniel will include an assert_helpful_error_message method in
the next release of Test::Unit.

> And while I suspect you're being a little flipant, I like your
> implementation - it does do nearly everything that's desired.

What?  Me flippant?  Never!

;)

--
-- Jim Weirich
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-03-14 20:04
(Received via mailing list)
Jim Weirich wrote:
...
> -- BEGIN ABSTRACT METHOD IMPLEMENTATION ---------------------------
>
> class Module
>   def abstract_method(*method_names)
>   end
> end
>
> -- END ABSTRACT METHOD IMPLEMENTATION -----------------------------

It would be nice if it made the following fail somehow:

class A
  abstract_method :foo
  def foo; end
end

And also in this case:

class A
  def foo; end
  abstract_method :foo
end
This topic is locked and can not be replied to.