Pass by reference and copy on write

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

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.

On 10/3/10, Robert K. [email protected] wrote:

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.

Rick DeNatale wrote:

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 problem I have with this viewpoint is that you are then stuck with
a set of strange and arbitrary (seeming) rules about how things work.
Such as: A Fixnum is always a singleton object, whereas a Bignum is
not. For that matter why do we even have the distinction between
Fixnum and Bignum at all? Why isn’t there just one Integer class? If
you don’t understand the lower level representation, then these are
real head-scratchers. They’re just commandments from On High about How
It Shall Be. Whereas if you do know the underlying realities, then
it’s not only easier to remember all this stuff, it’s easier to reason
about it as well.

One of the important skills a programmer needs is the ability to work
on multiple levels of abstraction simultaneously. You need to be able
to zero in one one layer which is most relevant to the task while
simultaneously keeping the other layers in the back of your mind; not
completely forgotten, but just submerged and ready to come back to
the surface when the concerns relevant to that layer arise again.
Knowing as many of these layers as possible makes it easier to reason
about programs. I know it makes me a better programmer.

On 04.10.2010 19:31, Caleb C. wrote:

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 problem I have with this viewpoint is that you are then stuck with
a set of strange and arbitrary (seeming) rules about how things work.
Such as: A Fixnum is always a singleton object, whereas a Bignum is
not.

Initially you wouldn’t notice because transition from Fixnum to Bignum
is so smooth. But Fixnum and Bignum are just two classes. When I think
about Ruby’s model of referencing objects and passing references around
I usually do not focus on particular classes. Rather I talk about
instances and references - I don’t even need to think about particular
classes; if at all I would pick Object.

For that matter why do we even have the distinction between
Fixnum and Bignum at all? Why isn’t there just one Integer class? If
you don’t understand the lower level representation, then these are
real head-scratchers. They’re just commandments from On High about How
It Shall Be. Whereas if you do know the underlying realities, then
it’s not only easier to remember all this stuff, it’s easier to reason
about it as well.

That may be true when you want to reason about Fixnum, math and math
efficiency in Ruby. If you initially come to the language and try to
grasp how passing of objects around works and how aliasing effects occur
I find that point of view rather confusing than helpful because you need
to wrap your mind around several concepts (or “levels” as you say) at
the same time. I’d say this is more difficult for the average human
than focusing on one thing and then progressing to another.

One of the important skills a programmer needs is the ability to work
on multiple levels of abstraction simultaneously. You need to be able
to zero in one one layer which is most relevant to the task while
simultaneously keeping the other layers in the back of your mind; not
completely forgotten, but just submerged and ready to come back to
the surface when the concerns relevant to that layer arise again.
Knowing as many of these layers as possible makes it easier to reason
about programs. I know it makes me a better programmer.

You are right in the long term. But, as I said, it’s something
different if you consider learning a language (or even programming) from
scratch.

Kind regards

robert

On Mon, Oct 4, 2010 at 1:31 PM, Caleb C. [email protected] wrote:

references are opaque, and provide a barrier between the caller and
real head-scratchers. They’re just commandments from On High about How
It Shall Be. Whereas if you do know the underlying realities, then
it’s not only easier to remember all this stuff, it’s easier to reason
about it as well.

But singleton (or immediate) vs non immediate isn’t really the issue
here.

Fixnums and Bignums are both immutable, so if you are thinking at the
level of the language you can’t tell the difference in the context of
argument passing or assignment.

And just where the boundary is between Fixnums and Bignums is
implementation dependent, so it’s best when writing Ruby code not to
dwell on the fact that both exist.

Yes I know, you can make Fixnums mutable in a sense by adding
singleton methods which attach instance variables, but that’s a very
strange edge-case, and if you play stupid ruby tricks like that you
need to be able to deal with it.

One of the important skills a programmer needs is the ability to work
on multiple levels of abstraction simultaneously. You need to be able
to zero in one one layer which is most relevant to the task while
simultaneously keeping the other layers in the back of your mind; not
completely forgotten, but just submerged and ready to come back to
the surface when the concerns relevant to that layer arise again.
Knowing as many of these layers as possible makes it easier to reason
about programs. I know it makes me a better programmer.

Agreed, but it cuts both ways. I can understand that someone coming
to Ruby from the viewpoint of a C programmer is tempted to think in
terms of bits and bytes, but for approaching understanding Ruby one
needs to let go and think first at a higher level of abstraction.

In most cases, when programming Ruby one doesn’t really need to dive
that deeply into the implementation.


Rick DeNatale

Help fund my talk at Ruby Conf 2010:http://pledgie.com/campaigns/13677
Blog: http://talklikeaduck.denhaven2.com/
Github: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale