[RCR] abstract method in Ruby

L

Logan Capaldo

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


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.
--Apple-Mail-2-713170932--
 
N

nobu

Hi,

At Mon, 13 Mar 2006 13:10:45 +0900,
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
 
E

Erik Veenstra

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
:eek:k
end
end

[Bar1, Bar2].each do |klass|
begin
klass.new
rescue NotImplementedError => e
$stderr.puts e.message
end
end

----------------------------------------------------------------
 
A

ara.t.howard

Hi,

At Mon, 13 Mar 2006 13:10:45 +0900,


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
 
A

ara.t.howard

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
 
I

ilan berci

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
 
E

Erik Veenstra

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

ara.t.howard

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
 
A

Avdi Grimm

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"

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
 
I

ilan berci

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
 
A

ara.t.howard

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

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
 
P

Pit Capitain

(... implementing Module#abstract_method ...)

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

Regards,
Pit
 
L

Logan Capaldo

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


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.

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.
--Apple-Mail-3-765818685--
 
J

Jacob Fugal

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

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
 
J

Joel VanderWerf

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

nobu

Hi,

At Tue, 14 Mar 2006 01:01:31 +0900,
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

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
 
B

Bill Barnhill

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

Hmm, just some thoughts.
I think the abstract method idea nice in concept, but implementation perhap=
s
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 ms=
g
not
# responded to properly goes
here,
# nice to support YAML as
well..
should_respond_to :foo, :bar =3D> {}, :baz =3D> {:param_count =3D> 3}
end

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

B.new
=3Dbegin
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://=3DBill.Barnhill (For more info:
http://2idi.com/grs/index.php?referral_code=3Dcommunitivity )


Hi,

At Tue, 14 Mar 2006 01:01:31 +0900,


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

------=_Part_2660_15048088.1142344750320--
 
B

Bill Barnhill

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

Oops, my apologies, forgot an end in example.
Should read
class B < A
def initialize
@a =3D "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://=3DBill.Barnhill (For more info:
http://2idi.com/grs/index.php?referral_code=3Dcommunitivity
)

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 =3D> {}, :baz =3D> {:param_count =3D> 3}
end

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

B.new
=3Dbegin
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 th= e
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 al= so
doesn't match normative language use, perhaps a
.. must_only_respond_to


--Bill

Bill Barnhill
i-name: xri://=3DBill.Barnhill (For more info: http://2idi.com/grs/index.= php?referral_code=3Dcommunitivity
)





Hi,

At Tue, 14 Mar 2006 01:01:31 +0900,


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

------=_Part_2704_3165226.1142344982608--
 
D

dblack

Hi --

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.

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 ([email protected])
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
 
B

Bill Barnhill

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

Good points both, thanks.

If I have the constraint that objects within a certain group must be able t=
o
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 se=
e
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://=3DBill.Barnhill (For more info:
http://2idi.com/grs/index.php?referral_code=3Dcommunitivity
)

Hi --



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.


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 ([email protected])
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

------=_Part_3836_3654414.1142348114547--
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top