Oppinions on RCR for dup on immutable classes

Hi –

On Sat, 17 Feb 2007, [email protected] wrote:

d = obj.dup

in order to use d effectively.

I’m definitely not lobbying against understanding things :slight_smile:

David

On 2/16/07, Robert D. [email protected] wrote:

On 2/16/07, Phrogz [email protected] wrote:

b) You should never ever write “a.dup rescue a” because it will give
you strange problems if a might refer to a mutable object that doesn’t
respond to #dup.
Neve say never :wink: but when you write
a.dup rescue a
you should be aware of that potential danger

Right. My only point was that this solved what the OP asked for
without being hard.
It just seems like that solution is no better or worse than changing
the behaviour of dup, except for the fact that it puts the burden of
bad design into the programmer’s hands, and not that of the
programming language :slight_smile:

On Feb 16, 3:28 pm, “Gregory B.” [email protected] wrote:

Right. My only point was that this solved what the OP asked for
without being hard.
It just seems like that solution is no better or worse than changing
the behaviour of dup, except for the fact that it puts the burden of
bad design into the programmer’s hands, and not that of the
programming language :slight_smile:

IF the proposed implementation were to do what you suggested - rescue
every dup error by returning self - then I would agree that it’s a bad
design feature to put into the language.

I personally believe that returning self for symbols, fixnums, true,
false, and nil is not bad or dangerous in any way. Can you provide a
single example (even if contrived) where this could produce an
unintended result? I can only come up with one myself, and it’s too
contrived for me to consider it a danger.

Hi –

On Sat, 17 Feb 2007, [email protected] wrote:

you already do though?

h2[:point] << ’ that problem’

h2[:point] << ’ is to use marshal’

p h[:point]

harp:~ > ruby a.rb
“we already have that problem”
“we already have that problem”
“an imperfect solution”

I don’t consider returning a shallow copy the same as returning self.
It’s not the same object as the receiver of the “dup” message.

David

On Feb 16, 4:47 pm, “Gregory B.” [email protected] wrote:

“hi there”
def b.bar; “confusing?”; end

Imagine no error was thrown by that dup.

bar would be defined both on the duped (really not duped at all) nil
in your b variable, and on nil itself.

This of course is contrived, but it would be surprising behaviour. (I think)

A very good case. Enough so that I change my vote from “change dup” to
“leave things as they are”.

On 2/16/07, Phrogz [email protected] wrote:

I personally believe that returning self for symbols, fixnums, true,
false, and nil is not bad or dangerous in any way. Can you provide a
single example (even if contrived) where this could produce an
unintended result? I can only come up with one myself, and it’s too
contrived for me to consider it a danger.

the only things I can think of involve singleton methods

def nil.foo
“hi there”
end
=> nil
nil.foo
=> “hi there”
b = nil.dup
TypeError: can’t dup NilClass
from (irb):13:in `dup’
from (irb):13
def b.bar; “confusing?”; end

Imagine no error was thrown by that dup.

bar would be defined both on the duped (really not duped at all) nil
in your b variable, and on nil itself.

This of course is contrived, but it would be surprising behaviour. (I
think)

Gregory B. wrote:

On 2/16/07, Stefan R. [email protected] wrote:

|
That’s why I have 2 suggestions. The issue arises when you don’t know
those classes can’t really be duped (ie. return self) or expose that
transparently (not implement dup).

a = 3
=> 3
b = a.dup rescue a
=> 3
b
=> 3

What’s so bad about that?

As I said, I consider it a minor glitch. Of course you can work around
the problem, but a workaround is still only a workaround. IMHO the
“correct” way to do what you do above would be:

b = a.respond_to?(:dup) ? a.dup : a

If you have concerns about that beeing not as fast as rescuing your code
would still work (the raised exception would just be different).
Since that is my oppinion I posted this to the ML to see what other
oppinions about that are.
Besides, your above code assumes that the only reason a dup fails is due
to the singleton-immutables, what if dup fails due to another reason?
With your above code you’ll have a happy time tracking the bug as a
potential exception will never be raised.

My regards

Hi,

In message “Re: Oppinions on RCR for dup on immutable classes”
on Sat, 17 Feb 2007 01:45:17 +0900, Stefan R.
[email protected] writes:

|> Seconded. It pretty trivial for us core developers to make dup for
|> immutable objects to return themselves, but I don’t understand why
|> it is needed. I assume obj.object_id != obj.dup.object_id, and see no
|> good reason enough to break the assumption.

|That’s why I have 2 suggestions. The issue arises when you don’t know
|what you dup.

But your other suggestion was just removing dup altogether, right? In
that case, I see no fundamental difference from the current behavior
that raises TypeError with message “can’t dup” rather than
NoMethodError. Exception handling is your friend.

          matz.

Yukihiro M. wrote:

Hi,

In message “Re: Oppinions on RCR for dup on immutable classes”
on Sat, 17 Feb 2007 01:45:17 +0900, Stefan R.
[email protected] writes:

|> Seconded. It pretty trivial for us core developers to make dup for
|> immutable objects to return themselves, but I don’t understand why
|> it is needed. I assume obj.object_id != obj.dup.object_id, and see no
|> good reason enough to break the assumption.

|That’s why I have 2 suggestions. The issue arises when you don’t know
|what you dup.

But your other suggestion was just removing dup altogether, right? In
that case, I see no fundamental difference from the current behavior
that raises TypeError with message “can’t dup” rather than
NoMethodError. Exception handling is your friend.

          matz.

With current implementation, the only way to figure if an object can be
dup’ed is by dup it and catch the exception. I don’t know how imperative
duck-typing is for ruby, but this way actively prohibits it.
The method signature of those classes suggests it can be dup’ed, but
“hidden” in the code it actually shows that it isn’t. That’s in my
oppinion intransparent.
As said before, the issue can be worked around (as you say e.g. via
exception handling), but following your argument, my question would be:
why implement a method that can’t be executed?
I’ll restate again, that I consider it a minor issue. But I thought with
ruby2 and the cleanup of the APIs I thought it might be worth pointing.

My regards

On 2/17/07, Stefan R. [email protected] wrote:

As I said, I consider it a minor glitch. Of course you can work around
the problem, but a workaround is still only a workaround. IMHO the
“correct” way to do what you do above would be:

b = a.respond_to?(:dup) ? a.dup : a

You’re right, this is a better way. I don’t consider it a work
around. I consider it to be accounting for a language implementation
detail. (In that some objects cannot inherently be dup’ed)

If you have concerns about that beeing not as fast as rescuing your code
would still work (the raised exception would just be different).
Since that is my oppinion I posted this to the ML to see what other
oppinions about that are.
Besides, your above code assumes that the only reason a dup fails is due
to the singleton-immutables, what if dup fails due to another reason?
With your above code you’ll have a happy time tracking the bug as a
potential exception will never be raised.

If I wrote that code, I’d assume it was behaving as I intended, and be
sure to have tests to show it, but truthfully, the code you mentioned
is better.

It’s valid to consider that if you are dup’ing objects that shouldn’t
be duped ‘by accident’, then perhaps something has gone wrong in your
program design.

Hi,

In message “Re: Oppinions on RCR for dup on immutable classes”
on Sat, 17 Feb 2007 22:51:06 +0900, Stefan R.
[email protected] writes:

|With current implementation, the only way to figure if an object can be
|dup’ed is by dup it and catch the exception. I don’t know how imperative
|duck-typing is for ruby, but this way actively prohibits it.

As far as I understand, DuckTyping is not something based on method
existence, but something letting them raise error without explicit
type checking.

|As said before, the issue can be worked around (as you say e.g. via
|exception handling), but following your argument, my question would be:
|why implement a method that can’t be executed?

We haven’t implemented a method that can’t be executed. Object#dup
just fails if the object is not able to be duped. Am I missing
something?

          matz.

On Sat, 17 Feb 2007, Stefan R. wrote:

With current implementation, the only way to figure if an object can be
dup’ed is by dup it and catch the exception. I don’t know how imperative
duck-typing is for ruby, but this way actively prohibits it. The method
signature of those classes suggests it can be dup’ed, but “hidden” in the
code it actually shows that it isn’t. That’s in my oppinion intransparent.
As said before, the issue can be worked around (as you say e.g. via
exception handling), but following your argument, my question would be: why
implement a method that can’t be executed?

because, in ruby, all methods can be changed all the time:

class C
def dup() raise “now you can’t” end
end

c = C.new

c.dup rescue nil

class << c
def dup() raise “now you can” end
end

c.dup

just because an object responded to a message one time, at compile time,
or
whatever - doesn’t mean it will later. this is the dynamic in ‘dynamic
languages.’

note that being able to dump singleton classes doesn’t change this one
bit.

regards.

-a

On 2/17/07, Dean W. [email protected] wrote:

I’m probably missing some important points, as I don’t know the
intricasies of Ruby as well as many of you do.

POLS when applied to Ruby refers to Matz’s surprise. In this thread
and others, we need to remind people of that. Ruby is easy to
change, for your own needs. Feel free :slight_smile:

On 2/17/07, Yukihiro M. [email protected] wrote:

existence, but something letting them raise error without explicit
matz.
For me, this does break the POLS, because it breaks the Liskov
Substitution Principle (loosely, “an object of a derived type can be
substituted for an object of a base type”, for those of you who
haven’t heard of it…). LSP is essentially the underpinnings of
Bertrand Meyer’s “Design by Contract”.

If I’m iterating through a collection of objects and all of them
respond_to? a method, yet an exception is thrown in some cases, that’s
surprising :wink: The deep-copy scenario is a good one. In this case, it
would be nice to have a succinct way of copying the object graph
without having to worry about some dup methods “not working”. Perhaps
the “another” method suggestion would work. Perhaps thinking through
all the scenarios of why you would dup an object would suggest
refinements to its behavior. Off hand, I’m wondering if it would be so
bad for an immutable object, like a Fixnum, to allow a.object_id? ==
a.dup.object_id?.

I’m probably missing some important points, as I don’t know the
intricasies of Ruby as well as many of you do. This is a typical
dilemma with rich base classes and modules. Occasionally, the rich
“contract” can’t be satisfied transparently by all derivatives. Maybe
that’s just the price we have to pay for being rich :wink:

Dean W.
http://www.objectmentor.com
http://www.aspectprogramming.com
http://www.contract4j.org

unknown wrote:

because, in ruby, all methods can be changed all the time:

class C
def dup() raise “now you can’t” end
end

c = C.new

c.dup rescue nil

class << c
def dup() raise “now you can” end
end

c.dup

just because an object responded to a message one time, at compile time,
or
whatever - doesn’t mean it will later. this is the dynamic in ‘dynamic
languages.’

note that being able to dump singleton classes doesn’t change this one
bit.

regards.

-a

Care to explain why you chose defining a method with the sole purpose of
raising an exception over removing the method instead?

My regards

Gregory B. wrote:

If I wrote that code, I’d assume it was behaving as I intended, and be
sure to have tests to show it, but truthfully, the code you mentioned
is better.

It’s valid to consider that if you are dup’ing objects that shouldn’t
be duped ‘by accident’, then perhaps something has gone wrong in your
program design.

My issue is that I can’t test that. I can only try and catch the
exception. With dup that’s not too terrible as it doesn’t have
side-effects. Still in my oppinion having to run code to see what
happens is not a clean behaviour.

My regards

On 2/17/07, Stefan R. [email protected] wrote:

My issue is that I can’t test that. I can only try and catch the
exception. With dup that’s not too terrible as it doesn’t have
side-effects. Still in my oppinion having to run code to see what
happens is not a clean behaviour.

Oh, good point.

1.respond_to?(:dup)
=> true

Then you need to rescue the exception.

If you really need this behaviour, you could just stick it in a file
somewhere and require it.

class Fixnum
undef :dup
end
=> nil
2.dup
NoMethodError: undefined method `dup’ for 2:Fixnum

BTW, Matz, the only difference I suppose is that by having a
NoMethodError, we could make use of respond_to? as a check, but I
don’t know if I think it’s such a big deal that I’d be in support of a
RCR.

Yukihiro M. wrote:

As far as I understand, DuckTyping is not something based on method
existence, but something letting them raise error without explicit
type checking.

Hm, we probably have a different understanding of DuckTyping then.

We haven’t implemented a method that can’t be executed. Object#dup
just fails if the object is not able to be duped. Am I missing
something?

          matz.

I’ll try to explain it differently, depict my issue. Say you go to a
restaurant, take a look at the card and see “Spagetthi”. You order
Spagetthi but the waiter just tells you “Oh, we don’t serve Spagetthi
here.”. You’d naturally ask “Why put it on the card then if you don’t
serve it at all?”

My regards

Hi,

In message “Re: Oppinions on RCR for dup on immutable classes”
on Sun, 18 Feb 2007 00:54:42 +0900, Stefan R.
[email protected] writes:

|Hm, we probably have a different understanding of DuckTyping then.

Perhaps.

|> We haven’t implemented a method that can’t be executed. Object#dup
|> just fails if the object is not able to be duped. Am I missing
|> something?

|I’ll try to explain it differently, depict my issue. Say you go to a
|restaurant, take a look at the card and see “Spagetthi”. You order
|Spagetthi but the waiter just tells you “Oh, we don’t serve Spagetthi
|here.”. You’d naturally ask “Why put it on the card then if you don’t
|serve it at all?”

I imagine the waiter telling you “Oh, we don’t serve Spaghetti
today”.

          matz.

Hi,

In message “Re: Oppinions on RCR for dup on immutable classes”
on Sun, 18 Feb 2007 00:15:20 +0900, “Dean W.”
[email protected] writes:

|For me, this does break the POLS, because it breaks the Liskov
|Substitution Principle (loosely, “an object of a derived type can be
|substituted for an object of a base type”, for those of you who
|haven’t heard of it…). LSP is essentially the underpinnings of
|Bertrand Meyer’s “Design by Contract”.

Please no POLS here. Considering whom you’re trying to persuade, it’s
no use.

As for LSP, if Object class has dup method and Fixnum the subclass of
Object does not have dup, doesn’t it break the LSP? Fixnum no longer
has all the methods that Object has.

          matz.