Oppinions on RCR for dup on immutable classes

I'm not sure what your point is, Robert. Is it:

a) See, there's no point in duplicating an immutable object, since the
only reason to #dup is so you can mutate the instance later, and
anything you tried to do to mutate it would fail later on.

or is it

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.

?

···

On Feb 16, 11:43 am, "Robert Dober" <robert.do...@gmail.com> wrote:

On 2/16/07, Gregory Brown <gregory.t.br...@gmail.com> wrote:
> >> a = 3
> => 3
> >> b = a.dup rescue a
> => 3
> >> b
> => 3

> What's so bad about that?

I would ask the question: Why do you dup a?
The answer is, I presume, because I will modify the duplicate and I
do not want to modify the original.
ok so let us follow our code

b= a.dup rescue a
..

b.mutating_method
what will happen now?

My perspective is:
If a duplicate of an immutable object is indistinguishable from the
original, then you would have no idea if #dup was returning an
internal duplicate or the same instance. And you wouldn't care (except
that you'd probably prefer it to conserve resources internally and
return the same instance).

The determining question for me, then, is:
How indistinguishable would two distinct instances of these immutable
objects be? The only case where code might break is if you had:
  b = a.dup
  h = { a.object_id => 1, b.object_id=> 2 }
and you were very unhappy if you ended up running over the key. I've
never personally used object_id for that purpose (or any other than
debugging), so I'm not sure how likely that is. I just use objects
themselves as hash keys, in which case equivalent instances already
behave the same (even of mutable objects!):

irb(main):001:0> a = ; b=a.dup
=>
irb(main):002:0> p a.object_id, b.object_id
23327420
23327410
irb(main):003:0> h = {a=>1, b=>2}
=> {=>2}

irb(main):013:0> a,b = 1234567890,1234567890
=> [1234567890, 1234567890]
irb(main):014:0> p a.class, a.object_id, b.class, b.object_id
Bignum
23346300
Bignum
23346270
=> nil
irb(main):015:0> h = {a=>1,b=>2}
=> {1234567890=>2}

···

On Feb 16, 11:53 am, dbl...@wobblini.net wrote:

On Sat, 17 Feb 2007, Phrogz wrote:
> ...
> Having said that, I personally would prefer for 3.dup to 'just work',
> returning an object that is equivalent to the original. Just because
> the new instance happens to be the same doesn't mean that it's bad -
> as an immutable object, the only way to tell is via object_id, anyhow.

It doesn't mean it's bad, but it does mean that it isn't a duplicate
:slight_smile: At least, I wouldn't think that dup is the right name for a
method that might actually return the receiver.

Hi --

Having said that, I personally would prefer for 3.dup to 'just work',
returning an object that is equivalent to the original. Just because
the new instance happens to be the same doesn't mean that it's bad -
as an immutable object, the only way to tell is via object_id, anyhow.

I've come across this situation when writing generic code to do a
'deep copy'. Instead of changing the semantics of #dup, why not have
some other method that means 'make a copy if you can but if the object
has immutable semantics then return a reference to self'.

As for a name for such a method, how about Kernel#another ?

But self isn't other than self.

I'm not sure how #dup, #clone, and #another should be related. Perhaps
#another should use #clone instead of #dup? Maybe there should be another
version of #another that uses clone semantics?

#another_another? :slight_smile:

David

···

On Sat, 17 Feb 2007, Gary Wright wrote:

On Feb 16, 2007, at 1:50 PM, Phrogz wrote:

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

Hi,

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.

···

In message "Re: Oppinions on RCR for dup on immutable classes" on Sat, 17 Feb 2007 22:51:06 +0900, Stefan Rusterholz <apeiros@gmx.net> writes:

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.

···

On 2/17/07, Stefan Rusterholz <apeiros@gmx.net> 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

···

On Sat, 17 Feb 2007, Stefan Rusterholz 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?

--
we can deny everything, except that we have the possibility of being better.
simply reflect on that.
- the dalai lama

Hi --

···

On Sat, 17 Feb 2007, Phrogz wrote:

On Feb 16, 11:53 am, dbl...@wobblini.net wrote:

On Sat, 17 Feb 2007, Phrogz wrote:

...
Having said that, I personally would prefer for 3.dup to 'just work',
returning an object that is equivalent to the original. Just because
the new instance happens to be the same doesn't mean that it's bad -
as an immutable object, the only way to tell is via object_id, anyhow.

It doesn't mean it's bad, but it does mean that it isn't a duplicate
:slight_smile: At least, I wouldn't think that dup is the right name for a
method that might actually return the receiver.

My perspective is:
If a duplicate of an immutable object is indistinguishable from the
original, then you would have no idea if #dup was returning an
internal duplicate or the same instance. And you wouldn't care (except
that you'd probably prefer it to conserve resources internally and
return the same instance).

The determining question for me, then, is:
How indistinguishable would two distinct instances of these immutable
objects be? The only case where code might break is if you had:
b = a.dup
h = { a.object_id => 1, b.object_id=> 2 }
and you were very unhappy if you ended up running over the key. I've
never personally used object_id for that purpose (or any other than
debugging), so I'm not sure how likely that is. I just use objects
themselves as hash keys, in which case equivalent instances already
behave the same (even of mutable objects!):

irb(main):001:0> a = ; b=a.dup
=>
irb(main):002:0> p a.object_id, b.object_id
23327420
23327410
irb(main):003:0> h = {a=>1, b=>2}
=> {=>2}

irb(main):013:0> a,b = 1234567890,1234567890
=> [1234567890, 1234567890]
irb(main):014:0> p a.class, a.object_id, b.class, b.object_id
Bignum
23346300
Bignum
23346270
=> nil
irb(main):015:0> h = {a=>1,b=>2}
=> {1234567890=>2}

You could conceivably have a case where an object can't be dup'd, but
is mutable, like a singleton class. If dup returns self, then you
could end up changing the object when you didn't want to. It's
probably not an everyday problem... but I still don't like the idea of
having to remember that "dup" means "dup or self".

David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

> > >> a = 3
> > => 3
> > >> b = a.dup rescue a
> > => 3
> > >> b
> > => 3
>
> > What's so bad about that?
>
> I would ask the question: Why do you dup a?
> The answer is, I presume, because I will modify the duplicate and I
> do not want to modify the original.
> ok so let us follow our code
>
> b= a.dup rescue a
> ..
>
> b.mutating_method
> what will happen now?

I'm not sure what your point is, Robert. Is it:

a) See, there's no point in duplicating an immutable object, since the
only reason to #dup is so you can mutate the instance later, and
anything you tried to do to mutate it would fail later on.

Exactly
but I did not say that it was bad an idiom, it is not an idiom IMHO as
useful as it might look first.
AAMOF I was about to say first: Hey this idiom is great so no RCR is needed.
And then I thought... - hey that can happen :wink:
and I realized that these are strange cases to call dup to protect an
object from mutation and I want it that for immutable objects on the
fly. I am sure there are usecases, but view.

My conclusion was this is not an idiom worth a CR at all.

or is it

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

a.dup
will not really alert the user enough of that, I feel.

?

Strangely enough I fail to see the difference between a) and b)

Cheers
Robert

···

On 2/16/07, Phrogz <gavin@refinery.com> wrote:

On Feb 16, 11:43 am, "Robert Dober" <robert.do...@gmail.com> wrote:
> On 2/16/07, Gregory Brown <gregory.t.br...@gmail.com> wrote:

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 Wampler
http://www.objectmentor.com
http://www.aspectprogramming.com
http://www.contract4j.org

···

On 2/17/07, Yukihiro Matsumoto <matz@ruby-lang.org> wrote:

Hi,

In message "Re: Oppinions on RCR for dup on immutable classes" > on Sat, 17 Feb 2007 22:51:06 +0900, Stefan Rusterholz <apeiros@gmx.net> 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.

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

···

--
Posted via http://www.ruby-forum.com/\.

Gregory Brown 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

···

--
Posted via http://www.ruby-forum.com/\.

Yukihiro Matsumoto 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

···

--
Posted via http://www.ruby-forum.com/\.

Yes, but that would only be a problem if you did:
  class Object
    alias old_dup dup
    def dup
      self.old_dup rescue self
    end
  end

I would certainly not like that, either.

The OPs proposal, however, was to modify the behavior only for a
particular set of built-in immutable objects:
[NilClass, FalseClass, TrueClass, Fixnum, Symbol]

(Though, if you 'fix' Fixnum like this, you'll need to 'fix' Bignum
and Float and others as well to support duping.)

···

On Feb 16, 12:30 pm, dbl...@wobblini.net wrote:

You could conceivably have a case where an object can't be dup'd, but
is mutable, like a singleton class. If dup returns self, then you
could end up changing the object when you didn't want to. It's
probably not an everyday problem... but I still don't like the idea of
having to remember that "dup" means "dup or self".

you already do though?

harp:~ > cat a.rb

···

On Sat, 17 Feb 2007 dblack@wobblini.net wrote:

You could conceivably have a case where an object can't be dup'd, but is
mutable, like a singleton class. If dup returns self, then you could end up
changing the object when you didn't want to. It's probably not an everyday
problem... but I still don't like the idea of having to remember that "dup"
means "dup or self".

#
# crappy
#
   h = { :point => 'we already have' }

   h2 = h.dup

   h2[:point] << ' that problem'

   p h[:point]
#
# crappy
#
   h = { :point => 'we already have' }

   h2 = h.clone

   h2[:point] << ' that problem'

   p h[:point]
#
# sledge hammer
#
   h = { :point => 'an imperfect solution' }

   mcp = lambda{|obj| Marshal.load(Marshal.dump(obj))}

   h2 = mcp[ h ]

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

   p h[:point]

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

-a
--
we can deny everything, except that we have the possibility of being better.
simply reflect on that.
- the dalai lama

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 2/16/07, Robert Dober <robert.dober@gmail.com> wrote:

On 2/16/07, Phrogz <gavin@refinery.com> 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

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, Dean Wampler <deanwampler@gmail.com> wrote:

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

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.

···

On 2/17/07, Stefan Rusterholz <apeiros@gmx.net> wrote:

Hi,

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.

···

In message "Re: Oppinions on RCR for dup on immutable classes" on Sun, 18 Feb 2007 00:54:42 +0900, Stefan Rusterholz <apeiros@gmx.net> writes:

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

For me, this does break the POLS, because it breaks the Liskov
Substitution Principle

you are opening a can of worms (or ducks if you prefer).

I would say that LSP does not apply here simply because in Ruby we do
not have that kind of contract.
In order to apply LSP we need to say at at a point hey here we have an
object of class Base.(let the gods forgive me that I use Java)
void aMethod(final Base b){
   ....
}
and we expect this to work whenever we call aMethod with an object
that is a Base.
Anyway the compiler would not really allow otherwise.

SubClass sc; // subclassing Base od course
aMethod( sc ); // this is expected to work (from the type POV).

Such things just do not exist in Ruby, I believe that Ruby has
explained something to me:

OO Languages are Class oriented languages
Dynamic Languages are Object oriented languages.
Replace Class with Type and you see what I mean.

This is all very much IMHO of course but I feel that the Ruby
community has made me evolve a lot away from "Class oriented".

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

Do not let *your* code surprise *you*. If it does than it is not the
language's fault.
But I have seen the smiley :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".

Meaning not worrying about the semantics of your program ???
But you still have a good use case below, not good enough for a RCR but
good. --> see my last point too please.

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:

Instead of advocating for a RCR especially in the present scenario which is:
"Well that might be bad sometimes and goos sometimes, not really worth a CR"
you might just create your own little toolbox.
class Integer ... def dup etc.

Advocating is a good thing but it is not the only one.
After some months of using your toolbox you might still want the CR or
maybe throw it away, who knows?

Cheers
Robert

P.S.
I am against the CR but the discussion is a great one! Thumbs up!
R.

···

On 2/17/07, Dean Wampler <deanwampler@gmail.com> wrote:

On 2/17/07, Yukihiro Matsumoto <matz@ruby-lang.org> wrote:
> In message "Re: Oppinions on RCR for dup on immutable classes" > > on Sat, 17 Feb 2007 22:51:06 +0900, Stefan Rusterholz <apeiros@gmx.net> writes:

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

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important things.
-Anonymous

Hi,

···

In message "Re: Oppinions on RCR for dup on immutable classes" on Sun, 18 Feb 2007 00:15:20 +0900, "Dean Wampler" <deanwampler@gmail.com> 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.