Oppinions on RCR for dup on immutable classes

G

Gregory Brown

Neve say never ;) 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 :)
 
P

Phrogz

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

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

Gregory Brown

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.

the only things I can think of involve singleton methods
TypeError: can't dup NilClass
from (irb):13:in `dup'
from (irb):13
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)
 
P

Phrogz

the only things I can think of involve singleton methods


TypeError: can't dup NilClass
from (irb):13:in `dup'
from (irb):13


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

Stefan Rusterholz

Gregory said:
=> 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
 
S

Stefan Rusterholz

Yukihiro said:
Hi,

In message "Re: Oppinions on RCR for dup on immutable classes"
on Sat, 17 Feb 2007 01:45:17 +0900, Stefan Rusterholz

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

Gregory Brown

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

ara.t.howard

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
 
D

Dean Wampler

Hi,

In message "Re: Oppinions on RCR for dup on immutable classes"

|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 (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 ;) 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 ;)

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

Gregory Brown

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 :)
 
S

Stefan Rusterholz

unknown said:
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
 
S

Stefan Rusterholz

Gregory said:
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
 
G

Gregory Brown

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

Stefan Rusterholz

Yukihiro said:
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
 
R

Robert Dober

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 ;)

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

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

Stefan Rusterholz

Yukihiro said:
|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.

Um, no, with the classes mentioned in the RCR the answer would be
"never", not just "today". You'd have to buy the Restaurant and exchange
to cook to change the answer :)

My regards
 
A

ara.t.howard

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

yes - for illustration ;-)

a simpler example

harp:~ > cat a.rb
require 'singleton'

a = Object.new.instance_eval{ @x = 42 and self }
p a.dup

a.instance_eval{ class << self; include Singleton; end }
p a.dup


harp:~ > ruby a.rb
#<Object:0xb75cbb44 @x=42>
/home/ahoward//lib/ruby/1.8/singleton.rb:71:in `dup': can't dup instance of singleton Object (TypeError)
from a.rb:7


this is a side effect of strong dynamic type systems: the interpreter/compiler
cannot know until runtime if a methods signature makes sense becaues there are
an infinite variety of ways the semantics of a msg sent to an object might
change betweent two transmissions to the same object. if you don't like this
there are two choices:

- use language with a strong static type system. this rules out python,
java, c++, c, perl, lisp, and smalltalk

- dispair

regards.

-a
 
A

ara.t.howard

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.

you should check out haskell - it's one of only a few languages that can
provide this feature.

-a
 
A

ara.t.howard

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

because we serve it sometimes. in addition, most of our employees know how to
make it at any time. we might not have it today though. our we might, but
we're out of meatballs.

-a
 
A

ara.t.howard

Um, no, with the classes mentioned in the RCR the answer would be
"never", not just "today". You'd have to buy the Restaurant and exchange
to cook to change the answer :)

not really, you just have to learn how to cook! ;-)

harp:~ > cat a.rb
module DupSelf
def dup() self end
end

nil.extend DupSelf

p nil.dup

harp:~ > ruby a.rb
nil

regards.

-a
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top