[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: [Q] case based on object class

Peña, Botp

10/12/2004 3:42:00 AM

Robert Klemme [mailto:bob.news@gmx.net] wrote:

> The OO way is to use a mapping IMHO:
>
> ACTION_MAPPING = {
> Fixnum => lambda { puts "Fixnum" },
> Float => lambda { puts "Float" },
> String => lambda { puts "String" },
> }
>
> def myfunc(klass)
> code = ACTION_MAPPING[klass]
> raise ArgumentError, "Klass not found: #{klass}" unless code
> code.call
> end
>
> You can even define a block for the error as default
> effectively rendering the method a one liner like this:
>
> ACTION_MAPPING = Hash.new( lambda { raise ArgumentError,
> "Klass not found" } ).update(
> Fixnum => lambda { puts "Fixnum" },
> Float => lambda { puts "Float" },
> String => lambda { puts "String" }
> )
>
> def myfunc(klass) ACTION_MAPPING[klass].call end

hehe. I love posting of robert. very oo. c-oo-l.
thanks robert.

>
> Kind regards
>
> robert

kind regards -botp


12 Answers

Markus

10/12/2004 3:51:00 AM

0

> > The OO way is to use a mapping IMHO:
> >
> > ACTION_MAPPING = {
> > Fixnum => lambda { puts "Fixnum" },
> > Float => lambda { puts "Float" },
> > String => lambda { puts "String" },
> > }
> >
> > def myfunc(klass)
> > code = ACTION_MAPPING[klass]
> > raise ArgumentError, "Klass not found: #{klass}" unless code
> > code.call
> > end
> >
> > You can even define a block for the error as default
> > effectively rendering the method a one liner like this:
> >
> > ACTION_MAPPING = Hash.new( lambda { raise ArgumentError,
> > "Klass not found" } ).update(
> > Fixnum => lambda { puts "Fixnum" },
> > Float => lambda { puts "Float" },
> > String => lambda { puts "String" }
> > )
> >
> > def myfunc(klass) ACTION_MAPPING[klass].call end

But what about duck typing? This was asked (IIRC) earlier on this
thread but (again IIRC) not addressed. The duck typing way would be to
not depend on the class of the objects in the first place. In the given
example it would be something like:

puts x.class.to_s

which works for all classes, old, new, borrowed, blue.

If the original poster is still reading, what is the real
application? Is there some reason you need to know the class, or do you
just need to assure that the class has some behaviour?

-- Markus






Robert Klemme

10/12/2004 7:29:00 AM

0


"Markus" <markus@reality.com> schrieb im Newsbeitrag
news:1097553071.15571.670.camel@lapdog.reality.com...

> But what about duck typing? This was asked (IIRC) earlier on this
> thread but (again IIRC) not addressed. The duck typing way would be to
> not depend on the class of the objects in the first place. In the given
> example it would be something like:
>
> puts x.class.to_s
>
> which works for all classes, old, new, borrowed, blue.

I tried to cover this issue but maybe it hasn't become clear enough.
Although duck typing is appropriate in many cases, there are cases where
it's not and in fact degenerates a design. First it's important to note
that duck typing relies on the fact that all objects that can show up in a
certain context have a certain method (either as class or singleton
method) with the same signature:

# num must have #+(x) defined
def incr(num)
num + 1
end

Now, while this is certainly a good idea for methods like to_s, to_i etc.
it's a bad idea for functionality that is special to a part of your
application. For example, if you had a class Foo and defined methods
String#to_foo and Fixnum#to_foo etc. that might come in handy for a
certain lib or module and a lot of libraries do this, class interfaces of
standard classes get cluttered with methods totally specific to certain
parts of the application. Also the likeliness of name clashes increases.

Note also, that you introduce dependencies from standard lib classes to
application classes which can be dealt with in Ruby because of its dynamic
nature, but which generally point in the wrong direction: it's usually
cleaner to have an acyclic dependency graph (certain languages need this
because otherwise you'll have compilation problems, read Lakos' "Large
Scale C++ Software Design" on the matter).

> If the original poster is still reading, what is the real
> application? Is there some reason you need to know the class, or do you
> just need to assure that the class has some behaviour?

That'd be certainly interesting to learn.

Kind regards

robert

George Moschovitis

10/12/2004 8:06:00 AM

0

> If the original poster is still reading, what is the real
> application? Is there some reason you need to know the class, or do you
> just need to assure that the class has some behaviour?

here is a snippet from the real application (ndb). I want to make the
following code more elegant (I dont care for efficiency this code only
runs once :))

102 s, klass = prop.symbol, prop.klass
103
104 module_eval %{
105 def #{s}
106 return @#{s}
107 end
108
109 def #{s}=(val)
110 @#{s} = val
111 end
112
113 def __force_#{s}(val)
114 @#{s} = } + case klass.name
115 when Fixnum.name
116 "val.to_i()"
117 when String.name
118 "val.to_s()"
119 when Float.name
120 "val.to_f()"
121 when Time.name
122 "Time.parse(val.to_s())"
123 when TrueClass.name, FalseClass.name
124 "val.to_i() > 0"
125 else
126 "val"
127 end + %{
128 end
129 }
130 end


regards,
George Moschovitis

--
www.navel.gr | tel: +30 2106898050 | fax: +30 2106898437

Navel does not accept liability for any errors, viruses or omissions in
the contents of this message. The full corporate policy is available on
our site.

have fun: www.joy.gr

Markus

10/12/2004 3:47:00 PM

0

On Tue, 2004-10-12 at 00:34, Robert Klemme wrote:
> "Markus" <markus@reality.com> schrieb im Newsbeitrag
> news:1097553071.15571.670.camel@lapdog.reality.com...
>
> > But what about duck typing? This was asked (IIRC) earlier on this
> > thread but (again IIRC) not addressed. The duck typing way would be to
> > not depend on the class of the objects in the first place. In the given
> > example it would be something like:
> >
> > puts x.class.to_s
> >
> > which works for all classes, old, new, borrowed, blue.
>
> I tried to cover this issue but maybe it hasn't become clear enough.
> Although duck typing is appropriate in many cases, there are cases where
> it's not and in fact degenerates a design. First it's important to note
> that duck typing relies on the fact that all objects that can show up in a
> certain context have a certain method (either as class or singleton
> method) with the same signature:
>
> # num must have #+(x) defined
> def incr(num)
> num + 1
> end

Actually, what you are talking about is more "call it and hope it
works" than duck typing. The first part of duck typing is asking "does
it quack like a duck?"; before calling a method, you check to see if the
object responds to it (and perhaps check other properties that may be
needed to get the results you want).
It isn't a matter of eliminating conditional code completely, but
rather of basing the condition of what you care about (the methods)
rather than something only marginally related (the class).

> Now, while this is certainly a good idea for methods like to_s, to_i etc.
> it's a bad idea for functionality that is special to a part of your
> application. For example, if you had a class Foo and defined methods
> String#to_foo and Fixnum#to_foo etc. that might come in handy for a
> certain lib or module and a lot of libraries do this, class interfaces of
> standard classes get cluttered with methods totally specific to certain
> parts of the application. Also the likeliness of name clashes increases.

This is another thing that is not quite duck typing. Yes, it is
possible to extend classes to simplify duck typing (sometimes called
things like masquerading or class spoofing), but it isn't even needed
(though it often isn't as bad as you make it sound).

> Note also, that you introduce dependencies from standard lib classes to
> application classes which can be dealt with in Ruby because of its dynamic
> nature, but which generally point in the wrong direction: it's usually
> cleaner to have an acyclic dependency graph (certain languages need this
> because otherwise you'll have compilation problems, read Lakos' "Large
> Scale C++ Software Design" on the matter).

I don't see the relevance of this point. In some languages you
need to declare all identifiers before you use them. In some languages
you can not use negative numbers. But generalizing from these to ruby
is something deserving of more thought than just "it is tru in some
languages so..."

-- Markus



Markus

10/12/2004 4:05:00 PM

0

Very interesting.

This is a case where extending the base classes could certainly be
justified; adding a "from_s" method that was the inverse of "to_s" would
do the job nicely. It's tempting to use one of the other extant
techniques, but none of them have the requisite generality, so we write:

WARNING: CODE TYPED DIRECTLY INTO E-MAIL

def Object.from_s(s); s; end
def String.from_s(s); s.to_s; end
def Fixnum.from_s(s); s.to_i; end
def Float.from_s(s); s.to_f; end
def Time.from_s(s); Time.parse(s.to_s()); end
:
and so forth.

Then you can write something like "klass.from_s(val)" in your
routine. The main reason for doing it this way is the extensions to the
basic classes are of general utility; they are often called things like
"parse," "from," or "called" (e.g. Integer.called("48")). A second
reason for doing it this way is that it becomes easier to add additional
classes to the system (e.g. SQL enumerations) "on the fly".

-- Markus

On Tue, 2004-10-12 at 01:09, George Moschovitis wrote:
> > If the original poster is still reading, what is the real
> > application? Is there some reason you need to know the class, or do you
> > just need to assure that the class has some behaviour?
>
> here is a snippet from the real application (ndb). I want to make the
> following code more elegant (I dont care for efficiency this code only
> runs once :))
>
> 102 s, klass = prop.symbol, prop.klass
> 103
> 104 module_eval %{
> 105 def #{s}
> 106 return @#{s}
> 107 end
> 108
> 109 def #{s}=(val)
> 110 @#{s} = val
> 111 end
> 112
> 113 def __force_#{s}(val)
> 114 @#{s} = } + case klass.name
> 115 when Fixnum.name
> 116 "val.to_i()"
> 117 when String.name
> 118 "val.to_s()"
> 119 when Float.name
> 120 "val.to_f()"
> 121 when Time.name
> 122 "Time.parse(val.to_s())"
> 123 when TrueClass.name, FalseClass.name
> 124 "val.to_i() > 0"
> 125 else
> 126 "val"
> 127 end + %{
> 128 end
> 129 }
> 130 end
>
>
> regards,
> George Moschovitis



Robert Klemme

10/13/2004 9:04:00 AM

0


"Markus" <markus@reality.com> schrieb im Newsbeitrag
news:1097596015.20674.61.camel@lapdog.reality.com...
> On Tue, 2004-10-12 at 00:34, Robert Klemme wrote:
> > "Markus" <markus@reality.com> schrieb im Newsbeitrag
> > news:1097553071.15571.670.camel@lapdog.reality.com...
> >
> > > But what about duck typing? This was asked (IIRC) earlier on
this
> > > thread but (again IIRC) not addressed. The duck typing way would be
to
> > > not depend on the class of the objects in the first place. In the
given
> > > example it would be something like:
> > >
> > > puts x.class.to_s
> > >
> > > which works for all classes, old, new, borrowed, blue.
> >
> > I tried to cover this issue but maybe it hasn't become clear enough.
> > Although duck typing is appropriate in many cases, there are cases
where
> > it's not and in fact degenerates a design. First it's important to
note
> > that duck typing relies on the fact that all objects that can show up
in a
> > certain context have a certain method (either as class or singleton
> > method) with the same signature:
> >
> > # num must have #+(x) defined
> > def incr(num)
> > num + 1
> > end
>
> Actually, what you are talking about is more "call it and hope it
> works" than duck typing. The first part of duck typing is asking "does
> it quack like a duck?"; before calling a method, you check to see if the
> object responds to it (and perhaps check other properties that may be
> needed to get the results you want).
> It isn't a matter of eliminating conditional code completely, but
> rather of basing the condition of what you care about (the methods)
> rather than something only marginally related (the class).

I see we have a quite different understanding of Duck Typing. IMHO with
Duck Typing you just invoke a method and get bitten by the NoMethodError
if it's not there. After all, what do you want to do if you discover that
an instance does not support a method you expect it to? Through testing
you ensure that the programm is well behaved. And I think, my
understanding is close on what Dave and others say.

> > Now, while this is certainly a good idea for methods like to_s, to_i
etc.
> > it's a bad idea for functionality that is special to a part of your
> > application. For example, if you had a class Foo and defined methods
> > String#to_foo and Fixnum#to_foo etc. that might come in handy for a
> > certain lib or module and a lot of libraries do this, class interfaces
of
> > standard classes get cluttered with methods totally specific to
certain
> > parts of the application. Also the likeliness of name clashes
increases.
>
> This is another thing that is not quite duck typing.

Clearly, as I was not talking about Duck Typing here but instead about
certain design rationales independent of the typing model.

> Yes, it is
> possible to extend classes to simplify duck typing (sometimes called
> things like masquerading or class spoofing), but it isn't even needed
> (though it often isn't as bad as you make it sound).

Even worse, there are cases where it can do harm.

> > Note also, that you introduce dependencies from standard lib classes
to
> > application classes which can be dealt with in Ruby because of its
dynamic
> > nature, but which generally point in the wrong direction: it's usually
> > cleaner to have an acyclic dependency graph (certain languages need
this
> > because otherwise you'll have compilation problems, read Lakos' "Large
> > Scale C++ Software Design" on the matter).
>
> I don't see the relevance of this point. In some languages you
> need to declare all identifiers before you use them. In some languages
> you can not use negative numbers. But generalizing from these to ruby
> is something deserving of more thought than just "it is tru in some
> languages so..."

As I said, Ruby can cope with these kinds of cyclic dependencies. But
that does not make it good design if you have a standard class rely on
some kind of application part. Maybe I should have been more clear as to
the reasons behind this. Reasons, why one generally does not want to
modify standard classes with *specific* code:

- possible name clashes

- interface bloat, i.e., too many methods

- documentation (Where do you document these methods?
The documentation belongs to your app's documentation,
but the methods sit in std lib classes.)

- possible interference with the std class's behavior,
that might break other application parts

- introduction of bugs into parts that are assumend to
be thoroughly tested and thus quite bug free

I don't say it's *always* bad to modify standard classes. I say, it's
very questionable to modify standard classes with behavior that is
specific to a certain application. Of course, if you write a small script
this might not be a problem and if it's the most efficient way to arrive
at a solution, then doing so is certainly a good thing. But if you write
an application consisting of several components or if you even write a
library intended for general use, you should be very careful in modifying
standard classes.

Kind regards

robert


Markus

10/13/2004 3:10:00 PM

0

On Wed, 2004-10-13 at 02:04, Robert Klemme wrote:
> "Markus" <markus@reality.com> schrieb im Newsbeitrag
> news:1097596015.20674.61.camel@lapdog.reality.com...
> > On Tue, 2004-10-12 at 00:34, Robert Klemme wrote:
> > > "Markus" <markus@reality.com> schrieb im Newsbeitrag
> > > news:1097553071.15571.670.camel@lapdog.reality.com...
> > >
> > > > But what about duck typing? This was asked (IIRC) earlier on
> this
> > > > thread but (again IIRC) not addressed. The duck typing way would be
> to
> > > > not depend on the class of the objects in the first place. In the
> given
> > > > example it would be something like:
> > > >
> > > > puts x.class.to_s
> > > >
> > > > which works for all classes, old, new, borrowed, blue.
> > >
> > > I tried to cover this issue but maybe it hasn't become clear enough.
> > > Although duck typing is appropriate in many cases, there are cases
> where
> > > it's not and in fact degenerates a design.
> >
> > Actually, what you are talking about is more "call it and hope it
> > works" than duck typing. The first part of duck typing is asking "does
> > it quack like a duck?"; before calling a method, you check to see if the
> > object responds to it (and perhaps check other properties that may be
> > needed to get the results you want).
> > It isn't a matter of eliminating conditional code completely, but
> > rather of basing the condition of what you care about (the methods)
> > rather than something only marginally related (the class).
>
> I see we have a quite different understanding of Duck Typing. IMHO with
> Duck Typing you just invoke a method and get bitten by the NoMethodError
> if it's not there. After all, what do you want to do if you discover that
> an instance does not support a method you expect it to? Through testing
> you ensure that the programm is well behaved. And I think, my
> understanding is close on what Dave and others say.

Sure, if you had only one trick in your trick bag, you would just
call it and hope/expect it to work. That's the old "never test for an
error condition you don't know how to handle" rule. But if (as in the
case in question) you have multiple ways to do what you are trying to
do, some of them appropriate to some kinds of values, some appropriate
to others, you'd be nuts to just use the one and hope the other cases
never arose.

Instead, you'd either 1) test for method signatures with
responds_to? or 2) implement the most common case and then rescue
NoMethodError and try the rare cases. The first is (IMHO) was cleaner
but I have seen cases where the second was used effectively.

> > > Now, while this is certainly a good idea for methods like to_s, to_i
> etc.
> > > it's a bad idea for functionality that is special to a part of your
> > > application. For example, if you had a class Foo and defined methods
> > > String#to_foo and Fixnum#to_foo etc. that might come in handy for a
> > > certain lib or module and a lot of libraries do this, class interfaces
> of
> > > standard classes get cluttered with methods totally specific to
> certain
> > > parts of the application. Also the likeliness of name clashes
> increases.
> >
> > This is another thing that is not quite duck typing.
>
> Clearly, as I was not talking about Duck Typing here but instead about
> certain design rationales independent of the typing model.

So here is a language that gives you many workable ways to
accomplish a reasonable goal, but none of them are usable because they
every one of them violates some design principle or an other? Is it
possible that the set of design principles you are trying to employ is a
tad too restrictive?

If I want to write a polymorphic routine, but am not supposed to
test the class (since it provides no guarantee of behavour) or the
method signature, am not allowed to extend the core classes or sub class
them, how pray tell is it to be done?

> > Yes, it is
> > possible to extend classes to simplify duck typing (sometimes called
> > things like masquerading or class spoofing), but it isn't even needed
> > (though it often isn't as bad as you make it sound).
>
> Even worse, there are cases where it can do harm.

Agreed. I was thinking about taking 'rm' off my system for that
very reason, but then I thought--what if it somehow gets put back on?
How will I get rid of it then?

> > > Note also, that you introduce dependencies from standard lib classes
> > > to application classes which can be dealt with in Ruby because of its
> > > dynamic nature, but which generally point in the wrong direction: it's
> > > usually cleaner to have an acyclic dependency graph (certain languages
> > > need this because otherwise you'll have compilation problems,...)
> >
> > I don't see the relevance of this point. In some languages you
> > need to declare all identifiers before you use them. In some languages
> > you can not use negative numbers. But generalizing from these to ruby
> > is something deserving of more thought than just "it is tru in some
> > languages so..."
>
> As I said, Ruby can cope with these kinds of cyclic dependencies. But
> that does not make it good design if you have a standard class rely on
> some kind of application part. Maybe I should have been more clear as to
> the reasons behind this. Reasons, why one generally does not want to
> modify standard classes with *specific* code:
>
> - possible name clashes
>
> - interface bloat, i.e., too many methods
>
> - documentation (Where do you document these methods?
> The documentation belongs to your app's documentation,
> but the methods sit in std lib classes.)
>
> - possible interference with the std class's behavior,
> that might break other application parts
>
> - introduction of bugs into parts that are assumend to
> be thoroughly tested and thus quite bug free
>
> I don't say it's *always* bad to modify standard classes. I say, it's
> very questionable to modify standard classes with behavior that is
> specific to a certain application. Of course, if you write a small script
> this might not be a problem and if it's the most efficient way to arrive
> at a solution, then doing so is certainly a good thing. But if you write
> an application consisting of several components or if you even write a
> library intended for general use, you should be very careful in modifying
> standard classes.

If the behavior is specific to the application (and added by the
application) it can as well be encapsulated within the application. To
a first approximation, this will be the case simply because the code
which extends them lives in the application. If you aren't using it, it
isn't there. (Though looking back, I see that several of your points
seem to assume that this is not the case--e.g., the documentation
point.) If this is still worrisome, you can extend the interface only
of objects turned over to (or created by) your application. You can
even (if you really feel the need to go this far) remove them from any
objects you export. As for the name space issues, you can, if you like,
package your extensions in a companion object, "under-write" them (with
method missing, use reflection to detect and report collisions before
they occur, etc.)

All of these precautions can be neatly encapsulated themselves, so
you don't even need to mess with them while you're working on your code.

It really does seem to me that this is a "rule" rooted more in the
limitations of other languages than abstract software design principles
and not a good match for ruby. There are too many good ways to deal
with the potential problems to write the whole technique off with an
"almost never" prohibition.

For my part, I am not saying it is _always_ bad to extend standard
classes, but (if done judiciously) it can be a very useful technique,
and is no more dangerous than any other language feature of similar
power and generality.

-- Markus




Robert Klemme

10/14/2004 12:28:00 PM

0


"Markus" <markus@reality.com> schrieb im Newsbeitrag
news:1097680148.21256.85.camel@lapdog.reality.com...

> > I see we have a quite different understanding of Duck Typing. IMHO
with
> > Duck Typing you just invoke a method and get bitten by the
NoMethodError
> > if it's not there. After all, what do you want to do if you discover
that
> > an instance does not support a method you expect it to? Through
testing
> > you ensure that the programm is well behaved. And I think, my
> > understanding is close on what Dave and others say.
>
> Sure, if you had only one trick in your trick bag, you would just
> call it and hope/expect it to work. That's the old "never test for an
> error condition you don't know how to handle" rule. But if (as in the
> case in question) you have multiple ways to do what you are trying to
> do, some of them appropriate to some kinds of values, some appropriate
> to others, you'd be nuts to just use the one and hope the other cases
> never arose.
>
> Instead, you'd either 1) test for method signatures with
> responds_to? or 2) implement the most common case and then rescue
> NoMethodError and try the rare cases. The first is (IMHO) was cleaner
> but I have seen cases where the second was used effectively.

You are rigth: this seems to be an application of DT indeed. Although I
have to admit, that I find the one trick situation a much more elegant
application of DT than the multiple trick situation you describe. In
those cases I prefer to switch to a type mapping based approach (although
that has it's drawbacks in the light of singleton methods) like I
demonstrated in another posting (basically, create a HashMap with classes
as keys and lambdas as values).

> > > > Now, while this is certainly a good idea for methods like to_s,
to_i
> > etc.
> > > > it's a bad idea for functionality that is special to a part of
your
> > > > application. For example, if you had a class Foo and defined
methods
> > > > String#to_foo and Fixnum#to_foo etc. that might come in handy for
a
> > > > certain lib or module and a lot of libraries do this, class
interfaces
> > of
> > > > standard classes get cluttered with methods totally specific to
> > certain
> > > > parts of the application. Also the likeliness of name clashes
> > increases.
> > >
> > > This is another thing that is not quite duck typing.
> >
> > Clearly, as I was not talking about Duck Typing here but instead about
> > certain design rationales independent of the typing model.
>
> So here is a language that gives you many workable ways to
> accomplish a reasonable goal, but none of them are usable because they
> every one of them violates some design principle or an other? Is it
> possible that the set of design principles you are trying to employ is a
> tad too restrictive?

That may be, but please note that I didn't ban the "modify std class"
approach for all cases. I said that this should be done with care for
libs and larger apps.

> If I want to write a polymorphic routine, but am not supposed to
> test the class (since it provides no guarantee of behavour) or the
> method signature, am not allowed to extend the core classes or sub class
> them, how pray tell is it to be done?

See above for an example. However, it's difficult to discuss this without
a concrete example.

> If the behavior is specific to the application (and added by the
> application) it can as well be encapsulated within the application. To
> a first approximation, this will be the case simply because the code
> which extends them lives in the application.

Just note, that I explicitely mentioned large applications and libraries.

> If you aren't using it, it
> isn't there. (Though looking back, I see that several of your points
> seem to assume that this is not the case--e.g., the documentation
> point.) If this is still worrisome, you can extend the interface only
> of objects turned over to (or created by) your application. You can
> even (if you really feel the need to go this far) remove them from any
> objects you export.

In that case, you can as well use another class internally (possibly by
employing delegation).

> As for the name space issues, you can, if you like,
> package your extensions in a companion object, "under-write" them (with
> method missing, use reflection to detect and report collisions before
> they occur, etc.)

Hm... I prefer a simpler solution. After all, what would you do it you
had libA and libB which both add method String#foo and which both are able
to detect the name clash and report it? You'll most likely get an
exception and can't use libA and libB together. Of course, those libs
could be written to automatically choose appropriate names, possibly even
with dynamic generated prefix. But this likely becomes unmanageable fast.

> All of these precautions can be neatly encapsulated themselves, so
> you don't even need to mess with them while you're working on your code.
>
> It really does seem to me that this is a "rule" rooted more in the
> limitations of other languages than abstract software design principles
> and not a good match for ruby.

I don't think so. Name clashes and building proper components with clean
and consistent interfaces (i.e. interfaces that can be used by humans who
have to understand them) are not restricted to particular programming
languages and I think in fact are quite fundamental to software
engineering.

> There are too many good ways to deal
> with the potential problems to write the whole technique off with an
> "almost never" prohibition.

As you might have guessed, I don't regard the ways to cope with name
clashes of auxiliary methods in std classes as good as you do. :-) But
that's a personal judgement and we're all free to differ here.

> For my part, I am not saying it is _always_ bad to extend standard
> classes, but (if done judiciously) it can be a very useful technique,

I couldn't agree more. (Ooops, something wrong here? :-))

> and is no more dangerous than any other language feature of similar
> power and generality.

I leave that judgement for another thread. :-)

Thanks for taking the time to go through all this! I'm sorry that I'm so
stubborn about things that I'm convinced of.

Kind regards

robert

Markus

10/14/2004 5:50:00 PM

0

On Thu, 2004-10-14 at 05:29, Robert Klemme wrote:
> In those cases I prefer to switch to a type mapping based approach (although
> that has it's drawbacks in the light of singleton methods) like I
> demonstrated in another posting (basically, create a HashMap with classes
> as keys and lambdas as values).

> > If I want to write a polymorphic routine, but am not supposed to
> > test the class (since it provides no guarantee of behavour) or the
> > method signature, am not allowed to extend the core classes or sub class
> > them, how pray tell is it to be done?
>
> See above for an example. However, it's difficult to discuss this without
> a concrete example.

My main objection to this is that it is very brittle in the face of
sub-classing; if someone, for whatever reason, passes in a sub-class of
a class I am expecting (even if it is functionally identical) it will be
mishandled; the Hash of lambdas will return the default value, not the
value for the (identical) parent class.

> Hm... I prefer a simpler solution. After all, what would you do it you
> had libA and libB which both add method String#foo and which both are able
> to detect the name clash and report it? You'll most likely get an
> exception and can't use libA and libB together. Of course, those libs
> could be written to automatically choose appropriate names, possibly even
> with dynamic generated prefix. But this likely becomes unmanageable fast.

My solution (*smile* involving extending Symbol) doesn't get
unmanageable at all, so far as I've seen, uses:

class Symbol
@@unique_sym_counter = 0
def Symbol.unique(prefix="gloop")
prefix = prefix.to_s.
gsub(/\+/,'_plus_').
gsub(/\*/,'_times_').
gsub(/-/,'_minus_').
gsub(/\//,'_div_').
gsub(/\!/,'_bang_')
("#{prefix.to_s}__#{@@unique_sym_counter += 1}").intern
end
end

which is in part an example of:

> > All of these precautions can be neatly encapsulated themselves, so
> > you don't even need to mess with them while you're working on your code.
> >
> > It really does seem to me that this is a "rule" rooted more in the
> > limitations of other languages than abstract software design principles
> > and not a good match for ruby.
>
> I don't think so. Name clashes and building proper components with clean
> and consistent interfaces (i.e. interfaces that can be used by humans who
> have to understand them) are not restricted to particular programming
> languages and I think in fact are quite fundamental to software
> engineering.

With ruby you can have clean human visible interfaces which (at run
time) instantiate as name-clash free computer usable interfaces for a
small, one-time cost (defs become slightly slower).

> > There are too many good ways to deal
> > with the potential problems to write the whole technique off with an
> > "almost never" prohibition.
>
> As you might have guessed, I don't regard the ways to cope with name
> clashes of auxiliary methods in std classes as good as you do. :-) But
> that's a personal judgement and we're all free to differ here.
>
> > For my part, I am not saying it is _always_ bad to extend standard
> > classes, but (if done judiciously) it can be a very useful technique,
>
> I couldn't agree more. (Ooops, something wrong here? :-))

*laugh* Not at all. The main value of a thread like this is we
get to learn from each other, including learning how to understand how
the other thinks. Occasional agreement is just one of the many risks we
face.

> > and is no more dangerous than any other language feature of similar
> > power and generality.
>
> I leave that judgement for another thread. :-)
>
> Thanks for taking the time to go through all this! I'm sorry that I'm so
> stubborn about things that I'm convinced of.

No, thank you. Almost every valuable thing I know came from either
watching or listening to someone who was stubborn about things they
believe in. All in all, they came much cheaper than the small fraction
I acquired through (often painfully) personally discovering what didn't
work, and why.

-- Markus




Robert Klemme

10/15/2004 10:21:00 AM

0


"Markus" <markus@reality.com> schrieb im Newsbeitrag
news:1097776203.21256.213.camel@lapdog.reality.com...
> On Thu, 2004-10-14 at 05:29, Robert Klemme wrote:
> > In those cases I prefer to switch to a type mapping based approach
(although
> > that has it's drawbacks in the light of singleton methods) like I
> > demonstrated in another posting (basically, create a HashMap with
classes
> > as keys and lambdas as values).
>
> > > If I want to write a polymorphic routine, but am not supposed
to
> > > test the class (since it provides no guarantee of behavour) or the
> > > method signature, am not allowed to extend the core classes or sub
class
> > > them, how pray tell is it to be done?
> >
> > See above for an example. However, it's difficult to discuss this
without
> > a concrete example.
>
> My main objection to this is that it is very brittle in the face of
> sub-classing; if someone, for whatever reason, passes in a sub-class of
> a class I am expecting (even if it is functionally identical) it will be
> mishandled; the Hash of lambdas will return the default value, not the
> value for the (identical) parent class.

True, that's a problem. There's an easy fix on the cost of some
performance:

class ClassDispatcher < Hash
def [](cl)
cl.ancestors.each do |c|
meth = super(c) and return meth
end

raise NameError, "Not found for class #{cl}"
end
end

> > Hm... I prefer a simpler solution. After all, what would you do it
you
> > had libA and libB which both add method String#foo and which both are
able
> > to detect the name clash and report it? You'll most likely get an
> > exception and can't use libA and libB together. Of course, those libs
> > could be written to automatically choose appropriate names, possibly
even
> > with dynamic generated prefix. But this likely becomes unmanageable
fast.
>
> My solution (*smile* involving extending Symbol) doesn't get
> unmanageable at all, so far as I've seen, uses:
>
> class Symbol
> @@unique_sym_counter = 0
> def Symbol.unique(prefix="gloop")
> prefix = prefix.to_s.
> gsub(/\+/,'_plus_').
> gsub(/\*/,'_times_').
> gsub(/-/,'_minus_').
> gsub(/\//,'_div_').
> gsub(/\!/,'_bang_')
> ("#{prefix.to_s}__#{@@unique_sym_counter += 1}").intern
> end
> end
>
> which is in part an example of:
>
> > > All of these precautions can be neatly encapsulated themselves,
so
> > > you don't even need to mess with them while you're working on your
code.
> > >
> > > It really does seem to me that this is a "rule" rooted more in
the
> > > limitations of other languages than abstract software design
principles
> > > and not a good match for ruby.
> >
> > I don't think so. Name clashes and building proper components with
clean
> > and consistent interfaces (i.e. interfaces that can be used by humans
who
> > have to understand them) are not restricted to particular programming
> > languages and I think in fact are quite fundamental to software
> > engineering.
>
> With ruby you can have clean human visible interfaces which (at run
> time) instantiate as name-clash free computer usable interfaces for a
> small, one-time cost (defs become slightly slower).

I'm not sure whether I understand you correctly here - apparently there is
something missing: I understand that you generate unique symbols. It's
not completely clear to me how you integrate that. Taking the former
example of LibA and LibB which both want to add method "too" to class
String. How would that be done using this Symbol extension?

> > > There are too many good ways to deal
> > > with the potential problems to write the whole technique off with an
> > > "almost never" prohibition.
> >
> > As you might have guessed, I don't regard the ways to cope with name
> > clashes of auxiliary methods in std classes as good as you do. :-)
But
> > that's a personal judgement and we're all free to differ here.
> >
> > > For my part, I am not saying it is _always_ bad to extend
standard
> > > classes, but (if done judiciously) it can be a very useful
technique,
> >
> > I couldn't agree more. (Ooops, something wrong here? :-))
>
> *laugh* Not at all. The main value of a thread like this is we
> get to learn from each other, including learning how to understand how
> the other thinks. Occasional agreement is just one of the many risks we
> face.

Yeah, it's *really* tough. I hope it doesn't get worse and we discover
that we had the same opinion right from the start.... :-))

> > > and is no more dangerous than any other language feature of similar
> > > power and generality.
> >
> > I leave that judgement for another thread. :-)
> >
> > Thanks for taking the time to go through all this! I'm sorry that I'm
so
> > stubborn about things that I'm convinced of.
>
> No, thank you. Almost every valuable thing I know came from either
> watching or listening to someone who was stubborn about things they
> believe in. All in all, they came much cheaper than the small fraction
> I acquired through (often painfully) personally discovering what didn't
> work, and why.

You're welcome. A little comfort: things learned the hard way usually are
remembered better. :-)

Kind regards

robert