[RCR] abstract method in Ruby

Discussion in 'Ruby' started by kwatch, Mar 11, 2006.

  1. kwatch

    kwatch Guest

    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?

    --
    regards,
    kwatch
     
    kwatch, Mar 11, 2006
    #1
    1. Advertising

  2. kwatch

    kwatch Guest

    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.

    --
    regards,
    kwatch
     
    kwatch, Mar 11, 2006
    #2
    1. Advertising

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

    > 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.
    >
    > --
    > 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.
     
    Logan Capaldo, Mar 11, 2006
    #3
  4. kwatch

    Jacob Fugal Guest

    On 3/11/06, kwatch <> 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'."

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

    --
    Jacob Fugal
     
    Jacob Fugal, Mar 11, 2006
    #4
  5. kwatch

    kwatch Guest

    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


    Logan Capaldo wrote:

    > On Mar 11, 2006, at 5:23 PM, kwatch wrote:
    >
    > > 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.
    > >
    > > --
    > > 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.
     
    kwatch, Mar 12, 2006
    #5
  6. kwatch

    kwatch Guest

    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


    Jacob Fugal wrote:

    > On 3/11/06, kwatch <> 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'."
    >
    > > ----------
    > > 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.

    >
    > 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
    >
    > --
    > Jacob Fugal
     
    kwatch, Mar 12, 2006
    #6
  7. ------=_Part_6075_13950129.1142151951941
    Content-Type: text/plain; charset=ISO-8859-1
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: inline

    On 3/12/06, kwatch <> 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

    ------=_Part_6075_13950129.1142151951941--
     
    Meinrad Recheis, Mar 12, 2006
    #7
  8. --Apple-Mail-1-661157955
    Content-Transfer-Encoding: 7bit
    Content-Type: text/plain;
    charset=US-ASCII;
    delsp=yes;
    format=flowed


    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." :)
    --Apple-Mail-1-661157955--
     
    Logan Capaldo, Mar 12, 2006
    #8
  9. Re: abstract method in Ruby

    > 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/
     
    Erik Veenstra, Mar 12, 2006
    #9
  10. 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.
     
    Benjohn Barnes, Mar 12, 2006
    #10
  11. Hi,

    In message "Re: [RCR] abstract method in Ruby"
    on Sun, 12 Mar 2006 16:38:44 +0900, "kwatch" <> 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.
     
    Yukihiro Matsumoto, Mar 12, 2006
    #11
  12. kwatch

    Guest

    On Mon, 13 Mar 2006, Yukihiro Matsumoto wrote:

    > Hi,
    >
    > In message "Re: [RCR] abstract method in Ruby"
    > on Sun, 12 Mar 2006 16:38:44 +0900, "kwatch" <> 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.


    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

    --
    share your knowledge. it's a way to achieve immortality.
    - h.h. the 14th dali lama
     
    , Mar 13, 2006
    #12
  13. On Mon, Mar 13, 2006 at 09:16:30AM +0900, wrote:
    > On Mon, 13 Mar 2006, Yukihiro Matsumoto wrote:
    > >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 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

    --
    Mauricio Fernandez - http://eigenclass.org - singular Ruby
     
    Mauricio Fernandez, Mar 13, 2006
    #13
  14. kwatch

    Guest

    On Mon, 13 Mar 2006, Mauricio Fernandez wrote:

    > 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

    --
    share your knowledge. it's a way to achieve immortality.
    - h.h. the 14th dali lama
     
    , Mar 13, 2006
    #14
  15. kwatch

    kwatch Guest

    [ANN] abstract.rb 1.0.0 release (Re: abstract method in Ruby)

    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.

    --
    regards,
    kwatch
     
    kwatch, Mar 13, 2006
    #15
  16. kwatch

    kwatch Guest

    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.

    --
    regards,
    kwatch
     
    kwatch, Mar 13, 2006
    #16
  17. kwatch

    kwatch Guest

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


    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.

    --
    regards,
    kwatch
     
    kwatch, Mar 13, 2006
    #17
  18. kwatch

    Guest

    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

    --
    share your knowledge. it's a way to achieve immortality.
    - h.h. the 14th dali lama
     
    , Mar 13, 2006
    #18
  19. kwatch

    Guest

    On Mon, 13 Mar 2006, kwatch wrote:

    > 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

    --
    share your knowledge. it's a way to achieve immortality.
    - h.h. the 14th dali lama
     
    , Mar 13, 2006
    #19
  20. Hi,

    In message "Re: [RCR] abstract method in Ruby"
    on Mon, 13 Mar 2006 12:33:45 +0900, "kwatch" <> 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.
     
    Yukihiro Matsumoto, Mar 13, 2006
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sameer
    Replies:
    4
    Views:
    632
    Roedy Green
    Aug 31, 2005
  2. Chris Zopers

    Abstract method in non-abstract class

    Chris Zopers, Aug 8, 2008, in forum: ASP .Net
    Replies:
    2
    Views:
    400
    Göran Andersson
    Aug 8, 2008
  3. Stefan Ram
    Replies:
    27
    Views:
    794
    Stefan Ram
    Aug 17, 2009
  4. Replies:
    1
    Views:
    160
    Trans
    Feb 5, 2007
  5. Roger Pack
    Replies:
    2
    Views:
    108
    Phrogz
    Jan 2, 2010
Loading...

Share This Page