Oppinions on RCR for dup on immutable classes

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

one cannot
know in advance whether it’s ok to call something - even if the object
responds to it.

With this in mind, what do you guys think of this work around?

[sandal@metta payroll_camping]$ irb
class Object
def dup?
dup
rescue TypeError
false
end
end
=> nil
3.dup?
=> false
3.dup? || 3
=> 3
“foo”.dup?
=> “foo”

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

“not implemented”. Different story.
If you check previous messages you might figure that I can understand
the use in abstract classes (or mixins here) even though I’d do it
differently.

Actually, My point was about expressiveness through exceptions.
That is what you seem to be missing

3.dup
TypeError: can’t dup Fixnum
from (irb):1:in `dup’
from (irb):1

TypeError is different than NoMethodError. In this case, it is
expressive of what is actually going wrong. Again, the difference is
in the rescue.

On Sun, 18 Feb 2007, Gregory B. wrote:

dup

=> “foo”

same semantics - but nice and clean.

-a

[email protected] wrote:

=> nil

3.dup?
=> false

3.dup? || 3
=> 3

“foo”.dup?
=> “foo”

same semantics - but nice and clean.

+1

Hi –

On Sun, 18 Feb 2007, Gregory B. wrote:

dup

=> “foo”
false.dup? # => false :slight_smile:

I’m not sure what I think of it. I’m trying to think of how it would
be used, given that it’s a ?-method that returns something other than
true/false. Would you do this, for example:

if (d = x.dup?)

I’m not sure that reads very well; it really kind of conceals the fact
that d might be an actual dup of x (at least to my eyes). But I’m
probably not thinking of all the places it might be used.

David

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

“class-centric” vs. “object-centric” programming.

It’s true that I rarely ever run into LSP issues, even in Java code. I
think it reflects, in part, the general movement OOD towards
de-emphasizing inheritance in favor of composition through
abstractions, etc. I would rather have the richness of Object and
Kernel, even with some LSP issues.
Interesting - and comforting - to know.
I do not have the luxury to follow more than Ruby nowadays (and I try
to find time for Smalltalk very hard, in vein so far).

As someone else commented, perhaps the real issue is that
5.respond_to?(:dup) returns true yet 5.dup raises, so you have no way
of knowing in advance that you shouldn’t call dup.
That just came up recently and I agree that this is kind of inelegant
(euphemistically)
Maybe that would be a reasonable CR?

Cheers
Robert

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

(euphemistically)
gracious to explain why not (in the exception message).
relationship to the process of being converted into an array
(including the relationship of opting out of it but in a way that
tells you what’s going on).

I realize that two ranges are of the same class while, say, a string
and nil aren’t. But it’s not a class thing; it’s more about
collective (not necessary along class lines) behaviors and
expectations.
Very well put David. I can only agree, it is better behavior.
Sorry Ara if I missed that post of yours this thread is a little bit
complex.
R.

Hi –

On Sun, 18 Feb 2007, Robert D. wrote:

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

As someone else commented, perhaps the real issue is that
5.respond_to?(:dup) returns true yet 5.dup raises, so you have no way
of knowing in advance that you shouldn’t call dup.
That just came up recently and I agree that this is kind of inelegant
(euphemistically)
Maybe that would be a reasonable CR?

See Ara’s answer to Dean, though. respond_to? isn’t designed to tell
you what will happen when you call the method. Lots of objects
respond to methods by raising exceptions.

Maybe the reason people don’t like dup doing that is that it always
does it for some classes of object. But I think that’s in order to
leave no mysteries. dup is “advertised”, so to speak, as something
that every object can do. So when an object can’t do it, it’s more
gracious to explain why not (in the exception message).

It’s a lot like this:

irb(main):001:0> (0…10).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):002:0> (0.0…10.0).to_a
TypeError: can’t iterate from Float

Ranges with floats in them could just undef to_a and not respond, but
the expectation exists that all ranges have some meaningful
relationship to the process of being converted into an array
(including the relationship of opting out of it but in a way that
tells you what’s going on).

I realize that two ranges are of the same class while, say, a string
and nil aren’t. But it’s not a class thing; it’s more about
collective (not necessary along class lines) behaviors and
expectations.

David

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

I’m not sure that reads very well; it really kind of conceals the fact
that d might be an actual dup of x (at least to my eyes). But I’m
probably not thinking of all the places it might be used.

Let me just play devils advocate for a sec.

4.nonzero?
=> 4
0.nonzero?
=> nil

I just think that dup? is a good compromise for third party hacking.
Not an RCR.

That having been said, I’ve not used nonzero? and I too am expecting
booleans from things that end in ?, but that was the best thing I
could come up with :wink:

Say, why not just define Object#mutable? and solve the issue of not
knowing if dup is possible or not?

Sure, it only works to tell you about one possible reason it’s not,
but it seems like a good workaround, far better than, say, duping just
to check.

Aur S.

I personally would like to see immutable objects like Nil and Fixnum
return self if sent a dup message.

The problem with the currently model is that it makes it difficult for
the programmer to make a distinction between “by copy” (“by value”)
and “by reference”. The most obvious example to look at is instance
variables.

There are some cases where you want your instance variables to be set
“by reference”; that is, where you are interested in a particular,
specific object and want to keep track of it, including changes in its
value, over time. Most often the kind of object you want to pass in by
reference is a high-level object that encapsulates some kind of
complex state and behaviour.

There are also cases where you want your instance variables to be set
“by copy”; that is, where you are not concerned about the identity of
an object and only care about the value of the object at the time you
assign it to a variable. Most commonly the kinds of objects you’ll
want to pass by copy are simple, primitive objects, usually numbers,
strings and values like nil.

The trouble is not that Ruby passes everything “by reference” by
default, but that Ruby makes it hard for you to pass “by copy” (“by
value”) when you want to. Imagine an instance variable for which
you’ve defined an accessor using “attr_accessor”. By default this will
pass “by reference”.

If you want to pass “by copy” you have to manually write an accessor.
But if you write your accessor like this:

def my_var=(value)
@my_var = value.dup
end

You’ll get exceptions whenever you pass nil (a very common case, I
would imagine). Change it to this:

def my_var=(value)
@my_var = (value.respond_to? :dup ? value.dup : value)
end

This works for nil, but it’s not as readable because of the extra
punctuation. Try passing in a Fixnum though; you’ll get an exception
because Fixnum claims to respond to “dup” but complains when you
actually send the message (pretty surprising). So you have to do this:

def my_var=(value)
@my_var = value.dup rescue value
end

To me this seems like an awful lot of work every time you want an
instance variable to be “by copy” (“by value”) instead of “by
reference”. Yes, you’ll might have problems here if you pass in a
singleton-but-mutable object, but I assume that if you know enough
about what you’re doing to specifically want things to be passed in
“by copy” (“by value”) then you also know exactly what will happen
when try passing in a singleton-but-mutable object.

As a programmer coming from Objective-C one of the current behaviour
was one the most annoying things about Ruby. Now I just write my
accessors using “rescue” whenever I want “by copy” behaviour. I
probably wouldn’t have had to adopt this habit if classes like Nil and
Fixnum just returned self in response to the “dup” message. This is
the orthodox behaviour in Objective-C; in fact, even singleton-but-
mutable classes normally just return self if sent the “copy” message.

An even more elegant solution, however, would be to extend
“attr_accessor” and friends to allow the programmer to specify if
attributes should be set “by copy” or “by reference”. This is exactly
what the new Obejctive-C 2.0 provides. In those cases where you want
to override the default behaviour you would do a
“attr_accessor_bycopy :my_var” and Ruby would do the right thing. Of
course, there is nothing stopping me from writing my very own
“attr_accessor_bycopy” method, but it would be nice if it were a
feature of Ruby itself.

Cheers,
Greg

On 2/18/07, Marcello B. [email protected] wrote:

Please, compare

execution, if needed.

Sure, it only works to tell you about one possible reason it’s not,
but it seems like a good workaround, far better than, say, duping just
to check.

Why should “dup” behave differently than all the other methods that raise
exceptions when something exceptional happens? :slight_smile:

Because it’s something exceptional that is very predictable and we
might want to know about it in advance.

My workaround is an addition, not a modification. For most cases,
rescue TypeError is perfectly fine, but sometimes we might want to
validate that a received object is dup-able at the beginning of a
method, before doing some hard work that would only later require
duping it.

That’s why I support having a way to know if oyu’ll be able to dup.

my 0.02c

pub 1024D/8D2787EF 723C 7CA3 3C19 2ACE 6E20 9CC1 9956 EB3C 8D27 87EF

Speaking of which, why IS mutability of certain objects left as an
implementation detail? I think we assume mutability/immutability of
objects a LOT at coding time, and for a different implementation to do
otherwise would certainly have performance implications and probably
even make code written for say MRI break, isn’t it so?

Perhaps mutability/immutability should be in the spec?

Aur S.

Hi,

On Sunday 18 February 2007 13:16, SonOfLilit wrote:

Say, why not just define Object#mutable? and solve the issue of not
knowing if dup is possible or not?

In my opinion, this exposes at the ruby level an implementation detail
that
pollutes the language design, and you must add it to the “weird things
you
should remember”.

Please, compare

def a
obj = some_method
raise ‘hey!’ unless obj.mutable?
do_stuff obj.dup
rescue SomeMethodError
pull_out_the_power_cord
end

to

def a
do_stuff some_method.dup
rescue SomeMethodError
pull_out_the_power_cord
rescue TypeError
raise ‘hey!’
end

I think that the whole design is clear and makes sense, you can call a
method,
and you should catch exceptions, if you care about them. Or let them
stop
execution, if needed.

Sure, it only works to tell you about one possible reason it’s not,
but it seems like a good workaround, far better than, say, duping just
to check.

Why should “dup” behave differently than all the other methods that
raise
exceptions when something exceptional happens? :slight_smile:

my 0.02c

Hi,

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

|> If we see a class as a restaurant, your illustration makes sense. But
|> from my point of view, we don’t know the name of the restaurant, nor
|> its menu. We just sit and cry for “Spaghetti!”, and sometimes the
|> waiter says “I’m sorry”.

|The example translates to:
|Restaurant := Class
|Menucard := #methods
|Say “we don’t serve that” := raised Exception

I don’t deny your model, but it smells like static typing. In Ruby,
we do

obj.dup

not,

void foo(SomeClass obj) {
obj.dup();
}

We don’t see any class in Ruby example, we just see an object, and
order it to dup, assuming it can dup itself, without knowing whether
it is really able to dup. If it can’t, for some reason, it raises an
exception. The best illustration for the situation (in Ruby, not
being Java) is something like above “sit and order” model.

          matz.

Hi –

On Sun, 18 Feb 2007, Greg H. wrote:

specific object and want to keep track of it, including changes in its

@my_var = value.dup
punctuation. Try passing in a Fixnum though; you’ll get an exception
singleton-but-mutable object, but I assume that if you know enough
mutable classes normally just return self if sent the “copy” message.

An even more elegant solution, however, would be to extend
“attr_accessor” and friends to allow the programmer to specify if
attributes should be set “by copy” or “by reference”. This is exactly
what the new Obejctive-C 2.0 provides. In those cases where you want
to override the default behaviour you would do a
“attr_accessor_bycopy :my_var” and Ruby would do the right thing. Of
course, there is nothing stopping me from writing my very own
“attr_accessor_bycopy” method, but it would be nice if it were a
feature of Ruby itself.

One problem with this is that dup oeprations don’t fall precisely
along the lines of a by value/by reference/by copy categorization. If
you do this:

a = “hi”
b = a.dup

you’re assigning to b a reference to a dup of a. If you do this:

b = a

you’re assigning a reference (the one in a) by value to b. This stuff
won’t necessarily transliterate well from another language.

I’d rather just keep what’s happening on the surface: if you want a
dup of an object, then call dup on it (with error handling if
necessary). I think that’s better than add a new layer of terminology
to Ruby method names. (I’m also not sold on the idea of a method
called “dup” harboring the possibility of silently returning something
that isn’t a dup, as discussed earlier in the thread.)

Maybe we’ll go down the road of flags some day:

attr_reader(:dup => true)

or something…

David