[Q] case based on object class

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

···

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

     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

"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

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

···

On Tue, 2004-10-12 at 00:34, Robert Klemme wrote:

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

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

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

···

On Tue, 2004-10-12 at 00:34, Robert Klemme wrote:

"Markus" <markus@reality.com> schrieb im Newsbeitrag
news:1097596015.20674.61.camel@lapdog.reality.com...
> > "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

···

On Wed, 2004-10-13 at 02:04, Robert Klemme wrote:

> On Tue, 2004-10-12 at 00:34, Robert Klemme wrote:

"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. :slight_smile: 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. :slight_smile:

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

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

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

···

On Thu, 2004-10-14 at 05:29, Robert Klemme wrote:

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

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

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

Kind regards

    robert

···

On Thu, 2004-10-14 at 05:29, Robert Klemme wrote:

I have a feeling this is a deeper subject than I can adequately
address in an e-mail tonight, but now is when I am, so:

     I have tackled this problem at least three times in ruby. Each
time I came up with a different solution (in fact, it wasn't until I was
well into the third solution that I recognized the pattern). In each
case, there were the following similarities (ignoring the many
differences):

      * I had started with a prototype that extended the core classes
      * I wanted to build on the prototype but limit the extensions to
        core classes for "local use"
      * I wanted the extensions to be freely available to some body of
        code
      * I wanted hide them from outsiders, either to protect them, to
        protect myself, or to prevent name clashes
      * I wanted the extensions to appear as regular methods to the code
        that was allowed to call them
      * I wanted to be able to mostly forget about how it worked once I
        had it working

   The three solutions I've tried that come to mind as follows were
roughly:

1) Use proxy/wrapper objects

     In this situation I had initially worried about name-space clashes,
so had used the convention that all methods added to class C by libX
would be named "C#libX_foo"; after I had done a bit of this I realized
that I could add method C#libX that returned a proxy/wrapper object that
"knew" all the methods foo_1,foo_2, etc. Then I could write c.libX.foo
instead of c.libX_foo, but get the same results.
     After a bit of this I started having the wrapper objects handle all
the regular methods in the same way as it handled the extensions, so
that I wouldn't have to worry about when to add the libX. I wound up
always adding it, and then (as I refactored) "moving it up stream" so
that I never added it (IIRC, there was a brief chaos in my thinking at
this point where I was effectively calling things like
v.libX.libY.libX.foo because I was unclear about what I had or wanted).

     Once this settled out, I found that almost all of the libX/libY's
had vanished and I was left with code that mostly looked like what I had
started with.

2) Use a method dispatcher

     This was a more ambitious attempt to build on the above. I decided
to trap (using reflection) any attempts to add methods to the core
classes (and had some angst over defining what I meant by this). I then
used my symbol-uniqueification thing to rename the functions and built a
table for redirection via method missing. This was in part dictated by
an attempt to work with code that mimicked part of the semantics of
CLOS (the Common Lisp Object System) as well.
     The dispatcher would route messages to the proper extension based
on the caller (as well as on other information); libX would see the libX
version, libY would see the libY version, and others would get normal
method_missing behaviour. As I went on, the overhead began to seem
excessive/unneeded (although I don't recall doing any actual performance
testing) so I switched to only going the full dispatch route when there
was an actual conflict.
     Most of the CLOS style dispatching melted under duck typing
(*smile* my version) as well.

3) Extend the object as they come in

     The third situation already had a "taint-wall" of sorts, and so I
played with simply extending the individual objects as they came in.
Not a general solution, but it worked out nicely.

···

On Fri, 2004-10-15 at 03:24, Robert Klemme wrote:

"Markus" <markus@reality.com> schrieb im Newsbeitrag
> > Hm... I prefer a simpler solution. After all, what would you do
> > if 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.

> >
> > 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 "foo" to class
String. How would that be done using this Symbol extension?

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

     The conclusion: in at least three cases I started out with a design
that relied on extending core classes in application specific ways. In
each case I managed to keep the semantics but partition them so as to
minimize/eliminate the effects on outside code. None of them was
perfect but they were all relatively easy to implement and none of them
resulted in cluttering up the application code (except as I was working
out what I wanted). In short, all of them were acceptable.

     Thus my belief that it is safe to extend the core classes if that
seems like the right thing to do. In most cases it will turn out that
the extension is general enough to warrant inclusion in the
"extension.rb" trick bag, and most of the rest will never face the test
of libraryification. But if that awful fate befalls them, a little
meta-programming should be able to solve it.

     Ultimately (especially if this comes up again, now that I'm on the
watch for it) I would like an uber-solution. But even not knowing what
that would look like, I am comfortable extending classes as needed,
secure in the knowledge that ruby won't fail me in my hour(s) of need.

    -- Markus

P.S. I may have time for one or two quick rounds tomorrow, but after
that I'll be off-list for a week.

Markus,

thanks for taking the time for the lengthy explanation. Although I have
one or two open questions, it seems to me that we agree very much on this
and previous misunderstandings were probably due to lack of complete
understanding.

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

<snip/>

> > > 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 "foo" to class
> String. How would that be done using this Symbol extension?

     I have a feeling this is a deeper subject than I can adequately
address in an e-mail tonight, but now is when I am, so:

     I have tackled this problem at least three times in ruby. Each
time I came up with a different solution (in fact, it wasn't until I was
well into the third solution that I recognized the pattern). In each
case, there were the following similarities (ignoring the many
differences):

      * I had started with a prototype that extended the core classes
      * I wanted to build on the prototype but limit the extensions to
        core classes for "local use"
      * I wanted the extensions to be freely available to some body of
        code
      * I wanted hide them from outsiders, either to protect them, to
        protect myself, or to prevent name clashes
      * I wanted the extensions to appear as regular methods to the code
        that was allowed to call them
      * I wanted to be able to mostly forget about how it worked once I
        had it working

   The three solutions I've tried that come to mind as follows were
roughly:

1) Use proxy/wrapper objects

     In this situation I had initially worried about name-space clashes,
so had used the convention that all methods added to class C by libX
would be named "C#libX_foo"; after I had done a bit of this I realized
that I could add method C#libX that returned a proxy/wrapper object that
"knew" all the methods foo_1,foo_2, etc. Then I could write c.libX.foo
instead of c.libX_foo, but get the same results.
     After a bit of this I started having the wrapper objects handle all
the regular methods in the same way as it handled the extensions, so
that I wouldn't have to worry about when to add the libX. I wound up
always adding it, and then (as I refactored) "moving it up stream" so
that I never added it (IIRC, there was a brief chaos in my thinking at
this point where I was effectively calling things like
v.libX.libY.libX.foo because I was unclear about what I had or wanted).

Do you mean by "moving it upstream" that you did the wrapping at the point
where some instance entered the lib? Example for what I mean:

public
  def foo(o)
    bar( o.libX )
  end

private
  def bar(o)
    # "o" is already libX proxy
    o.method_that_is_specific_to_libx()
    o.standard_method_of_o_s_class()
  end

Interesting enough we seem to be able to use "::" instead of "." making
thinks like this possible:

"foo".libx::bar

Where libx will return the proxy which then can handle bar. This looks
like we had separated the method namespace of String methods. But I guess
you discovered that already.

     Once this settled out, I found that almost all of the libX/libY's
had vanished and I was left with code that mostly looked like what I had
started with.

2) Use a method dispatcher

     This was a more ambitious attempt to build on the above. I decided
to trap (using reflection) any attempts to add methods to the core
classes (and had some angst over defining what I meant by this). I then
used my symbol-uniqueification thing to rename the functions and built a
table for redirection via method missing. This was in part dictated by
an attempt to work with code that mimicked part of the semantics of
CLOS (the Common Lisp Object System) as well.
     The dispatcher would route messages to the proper extension based
on the caller (as well as on other information); libX would see the libX
version, libY would see the libY version, and others would get normal
method_missing behaviour.

So your dispatcher has some similarities with my map approach, probably
the main difference being the better syntactic integration (i.e. methods
directly callable vs. "map[o.class].call()").

As I went on, the overhead began to seem
excessive/unneeded (although I don't recall doing any actual performance
testing) so I switched to only going the full dispatch route when there
was an actual conflict.
     Most of the CLOS style dispatching melted under duck typing
(*smile* my version) as well.

So you did a local extension to Duck Typing (TM) as well? *shudder*
:-))

3) Extend the object as they come in

     The third situation already had a "taint-wall" of sorts, and so I
played with simply extending the individual objects as they came in.
Not a general solution, but it worked out nicely.

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

     The conclusion: in at least three cases I started out with a design
that relied on extending core classes in application specific ways. In
each case I managed to keep the semantics but partition them so as to
minimize/eliminate the effects on outside code. None of them was
perfect but they were all relatively easy to implement and none of them
resulted in cluttering up the application code (except as I was working
out what I wanted). In short, all of them were acceptable.

Yes, that's true. When I spoke about std class extensions that should be
done with care, then I meant exactly this: if it's done directly, then
those methods better fit the class's semantics (and ultimately migrate
into some kind of std lib - either Ruby std lib or extension.rb), if done
via mechanisms like you describe, much more extensions are acceptable.

     Thus my belief that it is safe to extend the core classes if that
seems like the right thing to do. In most cases it will turn out that
the extension is general enough to warrant inclusion in the
"extension.rb" trick bag, and most of the rest will never face the test
of libraryification. But if that awful fate befalls them, a little
meta-programming should be able to solve it.

And it *should* be done to avoid nasty effects unless we're talking about
small scale scripts.

     Ultimately (especially if this comes up again, now that I'm on the
watch for it) I would like an uber-solution. But even not knowing what
that would look like, I am comfortable extending classes as needed,
secure in the knowledge that ruby won't fail me in my hour(s) of need.

If this kind of situation (several libs extending core classes) was very
common. I don't oversee all Ruby libs and thus don't know how common that
is. From what I read here, I get the feeling that it's not too common.
Hm...

Kind regards

    robert