Pass by reference and copy on write

X

Xavier Noria

I see
=C2=A0http://www.ruby-forum.com/topic/41160

which has a fairly long explanation of passing stuff by reference and cop=
y on write(cow).

A very noisy thread.

Let's say I have
=C2=A0def som_func(x,y)
=C2=A0 x =3D 24
=C2=A0 y =3D 42
=C2=A0end
=C2=A0.
=C2=A0.

=C2=A0a=3D1
=C2=A0b=3D2
=C2=A0.
=C2=A0.
=C2=A0.
=C2=A0some_func(a,b)


and I want to have some_func change a to 24 and b to 42 ...

How do I do it?


You can't. Ruby, C, and Java are pass-by-value. With that method call
semantics what you ask is not possible. You certainly can change the
state of a mutable object, but that is unrelated.

Perl is pass-by-reference, you can do that in Perl.

This is jargon, the word "reference" in "pass-by-reference" has
nothing to do with the "references" that Java or Ruby manage. Same
term, different meanings.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I see
http://www.ruby-forum.com/topic/41160

which has a fairly long explanation of passing stuff by reference and copy
on write(cow).


Let's say I have
def som_func(x,y)
x = 24
y = 42
end
.
.

a=1
b=2
.
.
.
some_func(a,b)


and I want to have some_func change a to 24 and b to 42 ...

How do I do it?


Is there a good write-up on copy on write and how to change the values of
parameters?



It kind of depends what you're looking for here. As you've shown it, what
you want is not possible, but its not exactly clear what you want, so there
might be different reasons for this.


First, in languages like C and C++, where a local variable is a memory
location on the stack, that you know and can manipulate, this is possible to
do. You can go to that location in memory, and change that value.

#include <iostream>
using namespace std;

void printab(string preface , int a , int b) {
cout << preface << ", a=" << a << ", " << "b=" << b << endl;
return;
}

void cpp_style( int &a , int &b ) {
a = 24;
b = 42;
}

void c_style_pointers( int *a , int *b ) {
*a = 36;
*b = 63;
}

int main (int argc, char const *argv[]) {
int a=1 , b=2;
printab( "initially" , a , b );

cpp_style( a , b );
printab( "c++ style ref" , a , b );

c_style_pointers( &a , &b );
printab( "c style macro" , a , b );
return 0;
}

In both cases, the actual memory address of main's a and b are passed to the
function, which dereferences them every time it wants to do anything with a
and b. In fact, these two examples are identical under the covers, the
reference style just hides away some syntax and some of the capability.

In Ruby, you cannot do this, because you do not have access to variables,
only the objects they are referencing (though there are some ways you can
kind of get around this with metaprogramming, I don't think any of them will
be useful here, and they are probably generally frowned upon).



Alternatively, if you are not wanting to change which object a and b point
to, but rather the internals of that object, then you can do this generally.
Meaning that you can mutate the object that a and b are pointing to:

def change_value(a,b)
a.replace "replaced a"
b.replace "replaced b"
end

a = "initial a"
b = "initial b"
puts "initially: a=#{a.inspect}, b=#{b.inspect}"
puts "initial object ids: a=#{a.object_id}, b=#{b.object_id}"

change_value a , b

puts
puts "after: a=#{a.inspect}, b=#{b.inspect}"
puts "afterw object ids: a=#{a.object_id}, b=#{b.object_id}"


However, you can't do this in your example, because a and b are integers,
and integers are immutable. They have no mutating methods, anything that
might have mutated them will instead return a new integer. That is hwy I had
to use String to show you.

You could get around this by writing a wrapper class to allow them to be
mutable.

class MutableValue
def initialize(initial_value)
@value = initial_value
end
def to_s
@value.to_s
end
def replace(new_value)
@value = new_value
end
def inspect
"#<Mutable #{to_s}>"
end
end

def change_value(a,b)
a.replace 24
b.replace 42
end

a = MutableValue.new 1
b = MutableValue.new 2
puts "initially: a=#{a.inspect}, b=#{b.inspect}"
puts "initial object ids: a=#{a.object_id}, b=#{b.object_id}"

change_value a , b

puts
puts "after: a=#{a.inspect}, b=#{b.inspect}"
puts "afterw object ids: a=#{a.object_id}, b=#{b.object_id}"



Of course, you need to be careful with this, because it could behave in ways
that you are not anticipating.
 
X

Xavier Noria

I disagree. The reference in the phrase 'pass by reference' is the
same thing as the references which =C2=A0are stored in ruby variables. Ru= by
is (mostly) a pass by reference language.

I disagree (see my post), pass-by-reference means pass-by-aliasing.
Ruby references have no bearing with Ruby method call semantics.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Rather than inventing a new class to do pass-by-reference, I'd
advocate using one that already exists: Array. So then you could call
the method like this:

a = [1]
b = [2]
puts "initially: a=#{a[0]}, b=#{b[0]}"
change_value a , b
puts "after: a=#{a[0]}, b=#{b[0]}"
Interestingly, that would bring it very much in line with the C example,
since pointers and arrays in C are (almost) the same thing.

It's just that in the case of Fixnums (and a few other things) it is
not possible to have references at all; such objects are always stored
(and passed) by value

If it were pass by value, I would expect it to make a new object each time
it passes that object.

def mutate_myvar(remote)
remote.instance_eval { @myvar = "bye" }
end
def oid(remote)
remote.send :eek:bject_id
end
local = 1
local.class # => Fixnum
local.instance_eval { @myvar = "hi" }
local.instance_eval { @myvar } # => "hi"
mutate_myvar(local)
local.instance_eval { @myvar } # => "bye"
local.send:)object_id) == oid(local) # => true


So it doesn't make a new object, which means that we are dealing with the
same object after passing it. I would expect this is because the method
received a reference to the same object that main is using, and would thus
expect it to be pass by reference. I would agree that it behaves like pass
by value, because it has no mutators.
 
C

Caleb Clausen

Rather than inventing a new class to do pass-by-reference, I'd
advocate using one that already exists: Array. So then you could call
the method like this:

a = [1]
b = [2]
puts "initially: a=#{a[0]}, b=#{b[0]}"
change_value a , b
puts "after: a=#{a[0]}, b=#{b[0]}"
Interestingly, that would bring it very much in line with the C example,
since pointers and arrays in C are (almost) the same thing.

Right. I'm just a C hacker at heart.
It's just that in the case of Fixnums (and a few other things) it is

If it were pass by value, I would expect it to make a new object each time
it passes that object.

It does make a new object when you pass a Fixnum as a parameter; the
Fixnum is copied into a different cell in memory. Fixnums are
compressed so that the Fixnum value is stored within the reference to
it. So I guess Fixnums are kinda value and reference all wrapped up in
one burrito.

Convinced? I'm not sure I am.
def mutate_myvar(remote)
remote.instance_eval { @myvar = "bye" }
end
def oid(remote)
remote.send :eek:bject_id
end
local = 1
local.class # => Fixnum
local.instance_eval { @myvar = "hi" }
local.instance_eval { @myvar } # => "hi"
mutate_myvar(local)
local.instance_eval { @myvar } # => "bye"
local.send:)object_id) == oid(local) # => true

You have just demonstrated that Fixnums are not immutable after all,
since their instance variables can be changed. I had forgotten that.
Deliberately.

You do realize, don't you, that instance variables on Fixnums (and
Symbols, and nil/false/true) is an evil hack, to be avoided by all
sane and reasonable code? All instances of the Fixnum 1 share the same
set of instance vars... ulh.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

It does make a new object when you pass a Fixnum as a parameter; the
Fixnum is copied into a different cell in memory. Fixnums are
compressed so that the Fixnum value is stored within the reference to
it. So I guess Fixnums are kinda value and reference all wrapped up in
one burrito.

Convinced? I'm not sure I am.
How about in this case?

def add_ivar(remote)
remote.instance_eval do
@ivar = "added in function"
end
nil
end

local = 1
add_ivar(local)
local.instance_variables # => [:mad:ivar]

Here, if I understand you, you are saying that add_ivar's object "remote"
and main's object "local" are not the same object, because it creates a new
object when we pass local as a parameter. However, in add_ivar, we open up
remote and add an instance variable to it. At this point, that ivar should
exist on the add_ivar's "remote" and not on main's "local". When we return,
we pass back nil, just to ensure that add_ivar's "remote" is never an arg,
and should simply be discarded when scope leaves the add_ivar method.
However, back in main, we can see that the ivar that it added does in fact
exist on main's "local". How did it get there, since it is a different
object than the one we mutated?
def mutate_myvar(remote)

You have just demonstrated that Fixnums are not immutable after all,
since their instance variables can be changed. I had forgotten that.
Deliberately.

You do realize, don't you, that instance variables on Fixnums (and
Symbols, and nil/false/true) is an evil hack, to be avoided by all
sane and reasonable code? All instances of the Fixnum 1 share the same
set of instance vars... ulh.

Yes, this code is merely intended to illustrate why I think they are
pointing to the same object.
 
R

Ralph Shnelvar

[Note: parts of this message were removed to make it a legal post.]

Josh,

JC> If you have a specific use case in mind, it might be best to show what you
JC> are trying to do so that people can suggest an alternative approach.

I had no specific use.

I was trying to understand what "reference" means in Ruby.

So ... Thank you!!!!

Ralph
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I'm not sure about a few things in your post. You say:
On the other hand, in a pass-by-reference language the situation is:

+-----+
| a | --+
+-----+ | +-----+
+--> | 1 |
+-----+ | +-----+
| b | --+
+-----+

That's why you can implement swap in such languages.
But that is how Ruby does it. I've been playing with it, and think that
Fixnums are always singleton in Ruby (meaning only ever one instance of a
given Fixnum), so lets use Strings to see.
var1 = "abc"
var2 = var1
var1.object_id == var2.object_id # => true
var1[1] = "x"
var1 # => "axc"
var2 # => "axc"

Clearly, they are both referencing the same object, as they have the same
object_id, and when var1 mutates the object, var2 also sees the mutation. So
according to your definition here, Ruby is pass by reference.

Also, I don't understand what you mean by implementing a swap. Both
variables reference the same object, there doesn't seem to be anything to
swap.
*But I can change the integer a variable holds by passing a pointer in C!*

That is true, but you are not passing the integer, you are passing a
pointer to the integer. Since C is pass-by-value, if you had a variable
holding the pointer before the call, you can be totally certain the variable
will hold the same exact pointer after the call.
It seems that you are differentiating them based on whether the pointer is
passed implicitly or explicitly. In other words, in C, you know you passed
the pointer, in Perl, the language did it for you. As C++ does the same
thing that I think you are saying Perl does, here is a gist showing that
C++'s references generate the same assembly source as C's pointers.
http://gist.github.com/240499

*Summary*
The terms pass-by-value and pass-by-reference are about links from names to
storage areas, they have nothing to do with the references or pointers of
your language.
I don't really understand what is meant by "references or pointers of your
language"

-----

When I think of pass by value and pass by reference, using your diagram,
above, I mean that pass by reference passes the value of the variable a,
which is a memory location (reference) for the object 1. This is juxtaposed
with what I would call pass by value, where it passes the object 1 itself, a
costly maneuver given the size of some objects. Here is an example.


#include <stdio.h>
typedef struct { int attribute; } expensive_struct;

void print_struct( char* context , expensive_struct *s ) {
printf( "%-30s s is at 0x%lx, and has the attribute %d\n" ,
context , (unsigned long int)s , s->attribute );
}

void pass_by_ref(expensive_struct *s) {
s->attribute++;
print_struct( "inside pass_by_ref" , s );
}

void pass_by_value(expensive_struct s) {
s.attribute++;
print_struct( "inside pass_by_value" , &s );
}

expensive_struct
pass_by_value2(expensive_struct s) {
s.attribute++;
print_struct( "inside pass_by_value2" , &s );
return s;
}

int main() {
expensive_struct s = {0};
print_struct( "main, initial" , &s);

pass_by_ref(&s);
print_struct( "main, after pass by ref" , &s);

pass_by_value(s);
print_struct( "main, after pass by value" , &s);

s = pass_by_value2(s);
print_struct( "main, after pass by value2" , &s);
return 0;
}


Notice in pass_by_ref, the memory address is the same, they are both
pointing to the same object, like your diagram above. And main sees the
change to that object that pass_by_ref made. The memory address (reference)
was passed.

However, in pass_by_value, the memory address is different, and main cannot
see changes to the object that pass_by_value has made. This is because the
object (value) was passed instead of the reference.

To get around this, pass_by_value2 does the same thing, but passes the value
back, which is then written over the old value. Since s is an expensive
struct, maybe containing large quantities of data, this is a costly thing to
do. It would be so much more efficient if we only had to pass the 4 or 8
bytes that our system needed to keep track of a memory location. Then we
could allow our function to work on the object (value) while only having to
incur the expense of passing the address (reference).

-----

Then, within pass by reference, I think there are two kinds of references
that can be passed. The C++ / (based on your description) Perl style, where
the reference passed is to the memory address of the variable, or Ruby /
Java style, where the reference passed is to the memory address of the
Object. The difference here is that C++ / Perl have to dereference twice in
order to get to the object, and have the added capacity to alter the
variable itself.

So in C++ / Perl, it looks like: ref2 --> ref1 --> "object"
Where as in Java it looks like: ref2 --> "object" <-- ref1

And C++ gives you the syntax such that every time you use ref2, you feel
like you are using ref1, by dereferencing ref2 each time you use it.

So, I say that Ruby does pass by reference the Java way, and Perl does pass
by reference the C++ way. And C can do both, and can also do pass by value.
 
R

Rick DeNatale

I'm not sure about a few things in your post.
....

So, I say that Ruby does pass by reference the Java way, and Perl does pass
by reference the C++ way. And C can do both, and can also do pass by value.

I've always referred to the way languages like Smalltalk, Ruby, and
Java(when passing an object) pass arguments is "by object reference."

http://www.ruby-forum.com/topic/126256#564797

I don't recall where I first heard that term, but it might have been
in the literature around the Emerald, a distributed OO language
designed at the University of Washington in the mid-late 1980s.

See also

http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Data_structures
and
http://www.testingreflections.com/node/view/5126

for use of that term in the description of Python

http://effbot.org/zone/call-by-object.htm for a similar comment by
someone about CLU

although the actual name coined by Barbara Liskov for what appears to
be the same thing in CLU is "call by sharing" or "call by object" or
"call by object sharing"

Of all of these 'synonyms' perhaps call "by object" might be the best.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
C

Caleb Clausen

It does make a new object when you pass a Fixnum as a parameter; the
Fixnum is copied into a different cell in memory. Fixnums are
compressed so that the Fixnum value is stored within the reference to
it. So I guess Fixnums are kinda value and reference all wrapped up in
one burrito.

Convinced? I'm not sure I am.
How about in this case?

def add_ivar(remote)
remote.instance_eval do
@ivar = "added in function"
end
nil
end

local = 1
add_ivar(local)
local.instance_variables # => [:mad:ivar]

Here, if I understand you, you are saying that add_ivar's object "remote"
and main's object "local" are not the same object, because it creates a new
object when we pass local as a parameter. However, in add_ivar, we open up
remote and add an instance variable to it. At this point, that ivar should
exist on the add_ivar's "remote" and not on main's "local". When we return,
we pass back nil, just to ensure that add_ivar's "remote" is never an arg,
and should simply be discarded when scope leaves the add_ivar method.
However, back in main, we can see that the ivar that it added does in fact
exist on main's "local". How did it get there, since it is a different
object than the one we mutated?

I would say that passing a fixnum does indeed make a new 'copy' of it,
for that method call. BUT, since, as you say below, fixnums are
singletons, and there's only ever one 'instance' of any given fixnum.
Changes to the fixnum's state affect the value seen by the caller as
well.

There doesn't need to be a method call to see the paradox of fixnum
state sharing in effect. Watch:

var1=1
var2=1 #a fresh, new copy of the fixnum 1
var1.instance_eval{@x=:foo}
p var2.instance_eval{@x} #=>:foo #whoa!, var1 and var2 share state!

But this same behavior is not seen with other numeric classes, like
Float and Bignum.

Frankly, I think that it should not be possible to set instance
variables on the built-in immutable classes: Fixnum, Bignum,
true/false/nil, Float, Symbol.


I like Rick's terminology: pass by object reference. I think that
makes it a little clearer what is really going on. Then if you
understand the subtleties of how Fixnums are actually packed into
their object references, you'll understand what is going on, and why
they behave differently than other classes.
 
B

Brian Candler

Josh said:
It seems that you are differentiating them based on whether the pointer
is
passed implicitly or explicitly. In other words, in C, you know you
passed
the pointer, in Perl, the language did it for you.

I think the difference is whether you are passing a reference to the
variable, or to the object itself.

In this code:

var = "abc"
foo(var)

'var' holds a reference to a String object, and that reference is passed
(by value) to foo. foo never knows that the object reference came from
local variable 'var', nor does it have any way to change var (*).

In C, it is possible to take a pointer to the local variable itself (the
memory slot which is the local variable), and hence to make remote
changes to the variable.

int a = 123;
foo(&a);

...

void foo(int *a) {
(*a)++;
}

In C++ you can have a 'reference' to the local variable, which avoids
explicit dereferencing (and adds enormously to confusion).

In Perl you can have references to variables too:

my $a = 123;
foo(\$a);
print "$a\n";
exit 0;

sub foo {
my $vref = shift;
$$vref++;
}

But in ruby, you can't get a reference to a local variable or an
instance variable or anything else which holds an object reference.

If you are a C user, it may be easiest to think that:
* every Ruby value is a pointer [to an object]
* you cannot obtain a pointer to a local variable or an instance
variable. You can only copy the pointer value itself.

Fixnum/nil/false/true are special cases. Rather than creating an object
representing each value, and taking a pointer to that object, the value
is buried within the pointer itself.

Regards,

Brian.

(*) Unless you pass a Binding object explicitly, in which case it is
possible to eval code which affects local variables in other scopes.
 
R

Robert Klemme

It's just that in the case of Fixnums (and a few other things) it is
not possible to have references at all; such objects are always stored
(and passed) by value

If it were pass by value, I would expect it to make a new object each
time
it passes that object.

It does make a new object when you pass a Fixnum as a parameter; the
Fixnum is copied into a different cell in memory. Fixnums are
compressed so that the Fixnum value is stored within the reference to
it. So I guess Fixnums are kinda value and reference all wrapped up in
one burrito.

Convinced? I'm not sure I am.
How about in this case?

def add_ivar(remote)
remote.instance_eval do
@ivar = "added in function"
end
nil
end

local = 1
add_ivar(local)
local.instance_variables # => [:mad:ivar]

Here, if I understand you, you are saying that add_ivar's object "remote"
and main's object "local" are not the same object, because it creates a new
object when we pass local as a parameter. However, in add_ivar, we open up
remote and add an instance variable to it. At this point, that ivar should
exist on the add_ivar's "remote" and not on main's "local". When we return,
we pass back nil, just to ensure that add_ivar's "remote" is never an arg,
and should simply be discarded when scope leaves the add_ivar method.
However, back in main, we can see that the ivar that it added does in fact
exist on main's "local". How did it get there, since it is a different
object than the one we mutated?

I would say that passing a fixnum does indeed make a new 'copy' of it,
for that method call.

While this is technically true (because the reference actually *is* the
Fixnum value) I think this is a bad way to express it. The reason is
that it looks into implementation details of the interpreter when one
tries to describe a phenomenon of the language Ruby. It actually
complicates things more than necessary which IMHO hinders understanding.

I always look at it this way: assignments copy object references. A
method call also does assignments (to method parameters in this case).
Whether you are affected by any aliasing effects mainly depends on the
behavior of an object's methods. If they modify the instance then of
course everyone with the same object reference will see the effect. If
they only return new references instead of modifying the instance (as in
the case of Fixnum#+ and others) there are no aliasing effects.
BUT, since, as you say below, fixnums are
singletons, and there's only ever one 'instance' of any given fixnum.
Changes to the fixnum's state affect the value seen by the caller as
well.

There doesn't need to be a method call to see the paradox of fixnum
state sharing in effect. Watch:

var1=1
var2=1 #a fresh, new copy of the fixnum 1

I'd say it's not a copy of the Fixnum. It's a copy of the reference to
the singleton instance. Basically you can look at "1" as an expression
which returns a reference to an instance - and it happens to be
implemented in a way as to always return a reference to the same
instance for the same literal. It's different for "'1'" which always
constructs a new String instance.
var1.instance_eval{@x=:foo}
p var2.instance_eval{@x} #=>:foo #whoa!, var1 and var2 share state!

But this same behavior is not seen with other numeric classes, like
Float and Bignum.

I just noticed there seem to be some optimizations going on at least
since 1.8.7 for Floats:

irb(main):002:0> 5.times { p 1.0.object_id }
137086642
137086642
137086642
137086642
137086642
=> 5
irb(main):003:0> 1.0.object_id
=> 137075106
irb(main):004:0> 1.0.object_id
=> 137055198
irb(main):005:0>

Apparently the interpreter creates only one float per code location.
Frankly, I think that it should not be possible to set instance
variables on the built-in immutable classes: Fixnum, Bignum,
true/false/nil, Float, Symbol.

That would at least reduce the potential for confusion.
I like Rick's terminology: pass by object reference. I think that
makes it a little clearer what is really going on. Then if you
understand the subtleties of how Fixnums are actually packed into
their object references, you'll understand what is going on, and why
they behave differently than other classes.

I agree. But I think when talking about the Ruby language it is not
necessary - and in fact causes more confusion than clarity - to talk
about the subtleties of Fixnum internal handling. It is sufficient to
state that all integer expressions up to a certain size yield singleton
instances. Actually, as a user of the Ruby language that does not write
extensions in C you cannot tell that the interpreter does not allocate
an instance on the heap but derives the value from the reference itself.

Kind regards

robert
 
X

Xavier Noria

We can't just let our imagination go and build our own mental model of
this stuff.

The public contract of Java and Ruby says you only manipulate
references to objects, not the objects themselves. For example, the
JLS says in section 4.3.1:

An object is a class instance or an array.
The reference values (often just references) are pointers
to these objects, and a special null reference, which
refers to no object.
...
There may be many references to the same object. Most
objects have state, stored in the fields of objects
that are instances of classes or in the variables that
are the components of an array object. If two variables
contain references to the same object, the state of the
object can be modified using one variable's reference
to the object, and then the altered state can be observed
through the reference in the other variable.

So it is not that Java leaves room for us to interpret that a variable
"user" can hold a real User instance and that these opaque references
are implementation details. You can't store a real user instance in a
variable in Java. Period. The language only allows you to store
references (modulus primitive types etc.). The contract is clear, and
from it stuff follows.

Same for Ruby. The Ruby Specification says for instance:

A variable is denoted by a name, and refers to an object,
which is called the value of the variable. A variable
itself is not an object. While a variable can refer to
only one object at a time, an object can be referred to
by more than one variable at a time.

So it belongs to their contract that we are not storing objects,
rather we store some opaque things that *refer* to them.

Having that in mind assignments are crystal clear. Given

a =3D b =3D [] # (1)

the situation is that a and b hold a reference to the same array
object. We all agree with this. And it is imporatant because when we
are talking about initializing method parameters conceptually
something very similar to assignment is happening. The JLS says about
this for example:

When the method or constructor is invoked (=C2=A715.12),
the values of the actual argument expressions initialize
newly created parameter variables, each of the declared
Type, before execution of the body of the method or
constructor.

That's pass-by-value. And what is copied if we are passing object
references, is object references. They are kinda opaque, but they
belong to the public contract nonetheless and there's no doubt that's
what you're passing around. And that is what let you clearly
understand the implications of (1). And whay let you square why
pass-by-value and being able to modify a mutable object are unrelated
things.

In Perl the difference is obvious because for example @a holds an
actual array. We do not have actual arrays in Ruby or Python. If we
have in Perl

@a =3D (1);
@b =3D @a;

pushing to @b does *not* affect @a. They are actual arrays, not
references to arrays, and assignment copies. You can create references
with the backslash, which are scalar values akin to C pointers. And
more interestingly for this thread, you can create aliases:

@a =3D (1);
*b =3D *a;

Now @a and @b are variables that hold the same actual array. They are alias=
es.

It's been a very long time since I did C++, but if I am not mistaken
it offers both semantics, pass-by-value (which may involve a copy
constructor), and pass-by-reference, which creates aliases as depicted
in my post.
 
R

Ralph Shnelvar

[Note: parts of this message were removed to make it a legal post.]

Brian,



Sunday, October 3, 2010, 12:11:28 PM, you wrote:

BC> (*) Unless you pass a Binding object explicitly, in which case it is
BC> possible to eval code which affects local variables in other scopes.

Could I ask you to explain this REALLY SLOWLY so that people like me can understand this ... PLEASE.

Ralph
 
R

Rick DeNatale

I like Rick's terminology: pass by object reference. I think that
makes it a little clearer what is really going on. Then if you
understand the subtleties of how Fixnums are actually packed into
their object references, you'll understand what is going on, and why
they behave differently than other classes.

I thing that the value of thinking of this as call by object
(reference) is that in languages like Smalltalk and Ruby the object
references are opaque, and provide a barrier between the caller and
the object's implementation. The only way for another object to get at
any state on the other side of that barrier is by sending a message to
the object, even if that message is instance_variable_get/set.

The confusion about thinking of call by value, vs. call by reference
in languages like Ruby is because sending a message to the object may
change its state, something which if the state is visible via message
sends will be observable via any reference to the object. So
immutable objects and mutable objects can confuse someone 'coming
from' C into thinking that one is passed by value, and the other by
reference, rather than the uniform mechanism actually in use.

The fact that some objects like Fixnums, nil, true, and false are
immediate values and usually don't have state other than their
identity is really an orthogonal thought, as is the ability in Ruby to
add 'singleton' instance variables to even objects represented by
immediate references.

And object references play in more than just argument passing
semantics. My dear friend from IBM, the late David N. Smith used to
say when teaching Smalltalk that all objects in Smalltalk are the
'same size', and I would say that the same thing is true in Ruby. By
this he didn't mean that all objects consumed the same amount of RAM
for their state, but that the references were all the same memory
size. So all instance variables are polymorphic object references,
arrays are heterogeneous, and there's always room for a different
breed of duck.

Like Dr. Who's Tardis, objects are usually bigger on the inside than
the outside.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
B

Brian Candler

Ralph said:
BC> (*) Unless you pass a Binding object explicitly, in which case it is
BC> possible to eval code which affects local variables in other scopes.

Could I ask you to explain this REALLY SLOWLY so that people like me can
understand this ... PLEASE.

A bit of code will speak louder than words.

def increment_a(b)
eval "a += 1", b
end

a = 123
increment_a(binding)
puts a # prints 124

What's going on?

Well, you probably know that a method (def .. end) starts a new local
variable scope, so normally inside method increment_a you would not have
access to any local variables defined outside.

The method Kernel#binding returns an instance of a Binding object, which
gives you access to the current local variable scope. We pass that
Binding object as a parameter to increment_a, which can then eval some
code in that scope.

What Binding gives you is access to the "activation record" - the place
where local variables are stored. In C this would be called the "stack
frame", but in Ruby it's more like a heap than a stack, since bits of it
can persist after the method returns.

Creating an explicit Binding object is one way to make the activation
record persist and/or pass it around for remote access.

You also get an implicit Binding whenever you pass a block:

def inc_a(&blk)
yield
eval "a += 1", blk.binding
end

a = 123
inc_a { puts "Hello" }
puts a

Another way to persist an activation record is to create a "closure".
Normally, when a method returns, its activation record is
garbage-collected, because all its local variables drop out of scope.
However, if you create an anonymous function which uses that activation
record, it will persist as long as that function persists.

def make_a_counter
a = 0
lambda { a += 1 }
end

c1 = make_a_counter
c2 = make_a_counter
puts c1.call # 1
puts c1.call # 2
puts c2.call # 1
puts c2.call # 2

Each time you call make_a_counter it creates its own local variable
scope, in which 'a' is defined. Then it returns a function which
increments that particular instance of 'a'.

So c1 and c2 are lambdas which increment different instances of 'a'.
They both carry state. You can even bootstrap objects this way. Closures
are a standard concept in many high-level languages, and you can google
for more info.

The point is: in Ruby, none of these mechanisms lets me obtain a pointer
or reference to the particular slot in the activation record which holds
the current value of variable 'a'. Therefore there is no "pass by
reference" of the kind you originally referred to.

But I *can* get a handle to the entire activation record, and then eval
code which manipulates 'a' directly.

Regards,

Brian.
 
B

Brian Candler

But I *can* get a handle to the entire activation record, and then eval
code which manipulates 'a' directly.

And I should add, this is a feature to be used highly sparingly -
perhaps if you were writing a debugger, say. PLEASE don't ever write
code like this:

def swap(var1, var2, b)
eval "#{var1}, #{var2} = #{var2}, #{var1}", b
end

x = 20
y = 5
swap("x","y",binding)
puts x
puts y
 

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,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top