DEV Community

Cover image for JavaScript, Ruby and C are not call by reference

JavaScript, Ruby and C are not call by reference

Derk-Jan Karrenbeld on June 27, 2019

🛑 This article is a response to various articles in the wild which state that JavaScript and Ruby are "Call/Pass by reference" for objects and "Ca...
Collapse
 
johncip profile image
jmc

Call by sharing is sometimes called call by object too, for the reasons you mentioned.

The term call-by-value is problematic.

To be fair, it made more sense in the 60s. The distinction mattered when (a handful of) languages had call-by-reference semantics, but they're dead now. The hip thing nowadays is to be nominally call-by-value while also providing a way of passing around things that can reasonably be called references, a la carte.

It's similar to how variables are scoped in JS... mostly lexical, but with exceptions. But languages used to be all one way or all the other, and people forget to say "with exceptions."

The reasons feel similar to me as well. I think that in the early days, it was natural to think of variables as not much more than the memory addresses they started out as. But it's easier to reason about values directly, and over the years we've abstracted up towards that. In Clojure even the collections are immutable, and its creator distinguishes between values and place-oriented programming.

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

To be fair, it made more sense in the 60s.

This is absolutely true! I didn't mean the term itself is a problem, but it's causing confusion, in today's day and age.

The distinction mattered when (a handful of) languages had call-by-reference semantics, but they're dead now

I mean, most languages where it still exists, it's super clear when you use it (C#, PHP, sorta rust etc).

Call by sharing is sometimes called call by object too, for the reasons you mentioned.

Thank you for the link! Some great content in there. I did not know this, but TIL.

Collapse
 
johncip profile image
jmc • Edited

Yeah, it's funny to see the Python longhairs having the same argument as everyone else. FWIW I'm with Tim Peters:

"Joe, I think our son might be lost in the woods"
"Don't worry, I have his social security number"

i.e. we care about the object, not the references. This is important too:

in Python, the variables in the formal argument list are bound to the actual argument objects. the objects are shared between caller and callee; there are no "fresh locations" or extra "stores" involved.

So not only are the "values" mutable at a distance, the references aren't even copied.

I think you could argue that Clojure, where by default (a) everything is immutable and (b) values are copied*, is properly call-by-value. I might grudgingly toss C in there too, since pointers are first-class there and the abstraction around structs and arrays is thin.

Others might be CbV, in a narrow sense, but their emphasis on stateful/mutable/referenced things violate the spirit of it. Java at least has some primitives which I imagine need to get copied. Python doesn't even do that...

There's a bit in a book called Theoretical Introduction to Programming that applies here, I think:

... to say that int is integer arithmetic with bounds and overflow conditions is to say that it is not integer arithmetic.

Anyway, I agree with you that the OO languages are not CbR w/r/t objects (though I think they can be in spirit), and IMO you did a good job of explaining the history and the nuance.


* Clojure uses persistent data structures for collections, so technically those aren't 😑

Thread Thread
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

i.e. we care about the object, not the references. This is important too:

Yes! This is super cool. Basically from my research I found out that Python, unlike Ruby and JavaScript, doesn't create a new "ref object" in the higher level language but actually directly assigns the reference, like C would.

Anyway, I agree with you that the OO languages are not CbR w/r/t objects (though I think they can be in spirit), and IMO you did a good job of explaining the history and the nuance.

❤❤ I also think you provide very valuable extra information!

I'm not very articulate at the moment but here is a meh response by me 😅 on the Python thang.

Valentin made a good comment and jmc went into more detail.

val_baca image

johncip image

Python isn't really an exception, but yes, I understand what you're saying. Looking through the python source code, it seems again that it is mostly what we are trying to say when no reference is copied. The re-assignment in python is actually copying a reference, but indeed, no new memory need be allocated, for that reference -- sorta, because the identifier (name) still needs to live... inside the memory.

So even though Python doesn't "copy" the reference like JavaScript does (create a new JSVal that points to the same object), it does so on a waaaay lower level (point directly to the original same object).

Ugh. It's giving me a slight headache 😅😅😅.

However, there are actually quite a few (mostly older) languages that don't copy at all, which would be those languages that are not call by value/sharing/object :).

The most interesting to me are copy-restore languages or those theoretical ones that only copy on write... a topic for another time.

Collapse
 
johncip profile image
jmc

I mean, most languages where it still exists, it's super clear when you use it (C#, PHP, sorta rust etc).

whoops, I meant to write "languages had default call-by-reference semantics"

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld • Edited

The post that sparked this article:

This is a great article with a lot of valuable information, especially if you're a beginner. Check it out!

Collapse
 
flrnd profile image
Florian Rand • Edited

Hey I really enjoyed your article, but, if I had to link every comment and article I read about design that "sparks" me, I would need a whole dev.to only for me.

You made your point and it was very instructive, but with all due respect, I find this last comment not necessary. I simple search can give hundres of examples.

Shameful edit 😅

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld • Edited

If you look at the original article you can see a discussion between me and that author and a link to this article.

Since that discussion was both very nice and interesting, and he asking me to link him this article so he could link that in his article, and the comment here is to link back to that -- no negative feelings whatsoever, which I understand this can be interpreted as :). I've edited the comment to make it more clear.

I actually think his original article, as stated in the comments there, is a great resource.

In general I completely agree with your statement, but the story here is, in my opinion, different.

Thread Thread
 
flrnd profile image
Florian Rand

Oh now It makes sense. I didn't read the comments on the other article and It looked like a totally diffent thing. Totally ashamed, my most sincere apologies.

Thread Thread
 
sleeplessbyte profile image
Derk-Jan Karrenbeld • Edited

It happens! We're all only human :)

Being on the spectrum, I find it very difficult.to.gauge these things so I rather have people call me out so that I can either fix it or apologise than not have that opportunity at all.

Thread Thread
 
flrnd profile image
Florian Rand

Yes, Next time, I'll keep my rule to comment Next day and not tired!

Collapse
 
ama profile image
Adrian Matei • Edited

I have trouble grasping the following section:

function reference_assignment(myRefMaybe) {
  myRefMaybe = { key: 42 }
}

var primitiveValue = 1
var someObject = { is: 'changed?' }

reference_assignment(primitiveValue)
primitiveValue
// => 1

reference_assignment(someObject)
// => { is: 'changed?' }
Enter fullscreen mode Exit fullscreen mode


`

As shown above, someObject has not been changed, because it was not a reference to someObject. In terms of the definitions before: it was not the memory
address of someObject that was passed, but a copy.

Isn't someObject not changed, because in the function the copy reference myRefMaybe is pointing to a totally new object { key: 42 }, and thus from this point on it will not modify the original, but if you would do myRefMaybe.is='indeed changed', you would see the results in someObject because the copy reference is referencing sameObject? Wouldn't this be more consistent with call by sharing definition...

I have written an article regarding this yesterday - Javascript call by value or by reference, actually by sharing, and would like to know if my mental model is flawed. Thanks

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

Call by sharing is explicitly mentioned at the bottom of this article and has, as far as my formal education goes, no consensus in definition, which is why I am hesitant in using that nomenclature, but yes. It is call by sharing given the definition you linked. That doesn't really change anything here.

Isn't someObject not changed, because in the function the copy reference myRefMaybe is pointing to a totally new object { key: 42 },

I don't quite understand this sentence. I think the point I was trying to make is that, if myRefMaybe was a reference, then re-assigning the value of that reference, would change the original definition too. But in JavaScript, it's a copy of a reference. Thus, re-assigning it doesn't change the original, but modifying the referenced object does change the original (as both the original and the argument / copy of the reference point to the same memory that stores the object).

Collapse
 
peerreynders profile image
peerreynders • Edited

A reference is simply a memory address that is automatically dereferenced by the language/runtime.

From that point of view

// (01) primitiveValue stores address #A
var primitiveValue = 1;
// (02) someObject stores address #B
var someObject = { is: 'changed?' };

// (03) myRefMaybe stores address #A from primitiveValue
reference_assignment(primitiveValue);
// (06) primitiveValue still has address #A
primitiveValue;

// (07) myRefMaybe stores address #B from someObject
reference_assignment(someObject);
// (10) someObject still has address #B
someObject;

function reference_assignment(myRefMaybe) {
  // (04) myRefMaybe still has address #A
  // (08) myRefMaybe still has address #B
  myRefMaybe = { key: 42 };
  // (05) myRefMaybe now has address #C
  // (09) myRefMaybe now has address #D
}
Enter fullscreen mode Exit fullscreen mode

if myRefMaybe was a reference, then re-assigning the value of that reference, would change the original definition too

To be able to change the "original definition" you would need the reference to the someObject reference itself (rather than the reference to the original object):

  • if someObject stores the address #B to the actual object
  • and that address #B is stored on behalf of someObject at address #Z
  • you need to place address #C at address #Z so that someObject now stores the address #C to that other object.

Update:

  • Call by Value: Copy the actual value.
  • Call by Sharing: Copy the address to the value.This makes it possible to share the value but not possible to replace the original value at the source variable.
  • Call by Reference: Copy the address of the "variable". This makes it possible to replace the original value at the source variable.

The way JavaScript behaves it actually never has to store anything at the address that is found in a variable - it only stores new addresses in variables.

  // (1) `myRefMaybe` has address #B as set by caller
  myRefMaybe = { key: 42 };
  // (2) New value `{ key: 42 }` is created
  // (3) `myRefMaybe` stores address #C to new value
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ama profile image
Adrian Matei

When you put it like this is more clear to me. Thanks

Collapse
 
iduoad profile image
Iduoad

This actually was a semester long discussion with a professor of mine 😁.

Here is how I think of it:

Functions are borowed from maths, and in maths functions always copy !

Let's consider the following real function: R->R, f:x->x2
with a=4 if we evaluate f(a)=16 the variable a will still be equal to 4.

In programming(To be clear in most programming languages[1]) functions always copy their arguments, then work the copy. i.e functions are passing_by_value creatures, they always copy what they got, and it's what they copy ("reference type" or "value type") which define if they will act on the state or not.

As @Valentin_Baca pointed out Java behave in the same way, python too :

def by_value(ob):
    ob = []

def by_reference(ob):
    ob.append(11)

li = [0]*4
print(li) #[0,0,0,0]
by_value(li)
print(li) #[0,0,0,0]
by_reference(li)
print(li) #[0,0,0,0,11]

1: python is an exception, it has mutables and immutables and it passes arguments to functions by assigning them.

Please correct me if I'm wrong

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

Valentin made a good comment and jmc went into more detail.

val_baca image

johncip image

Python isn't really an exception, but yes, I understand what you're saying. Looking through the python source code, it seems again that it is mostly what we are trying to say when no reference is copied. The re-assignment in python is actually copying a reference, but indeed, no new memory need be allocated, for that reference -- sorta, because the identifier (name) still needs to live... inside the memory.

So even though Python doesn't "copy" the reference like JavaScript does (create a new JSVal that points to the same object), it does so on a waaaay lower level (point directly to the original same object).

Ugh. It's giving me a slight headache 😅😅😅.

However, there are actually quite a few (mostly older) languages that don't copy at all, which would be those languages that are not call by value/sharing/object :).

The most interesting to me are copy-restore languages or those theoretical ones that only copy on write... a topic for another time.

Collapse
 
val_baca profile image
Valentin Baca

Same for Java.

The way I phrase it is: Java/JS/Ruby/C/etc are always pass by value; for objects the value is the reference.

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

In JS among others, the value is actually always a reference. V8 for example, boxes all primitives as JSValue which is a reference type in C.

That said, for general everyday use, your phrasing is perfectly fine when you're just trying to get stuff done :)

Collapse
 
rhymes profile image
rhymes • Edited

I agree with you wholeheartedly. Great article. The nomenclature doesn't help and this is not the only domain where better words and definitions would make things much easier, even for newcomers (see the concurrency domain for example)

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

Yes, concurrency/parallelism is still on my list to write about!

Collapse
 
bbarbour profile image
Brian Barbour

This is super duper informative and now I know a whole lot more!

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

Happy to write it!

I did try to look up a bit of the literature since you mentioned it was almost nowhere to be found. If I ever find a more modern or better source, I'll link it for ya!

Collapse
 
powerc9000 profile image
Clay Murray

Great write up.

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

Thank you! Looking for my next subject, so let me know if there are things you'd like to see.

Collapse
 
kkkoshumu profile image
Kosumu

At the replaceWithOne function body, did you mean list = [1]?
because if it is [] then I cannot observe the unchanged.

Sorry if I got it wrong I'm super newbie.
Thank you very much for the article!

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

No need to apologise for asking!

I've changed the example to be more clear. Does this answer your question?