Reference Variables

So, arrays/structs/objects are all passed by reference, while all “simple” variables are passed by value. What about adding an operator for arguments to force even simple variables to be passed by reference?

var myString = "jesse";

function append(required &initialString, required appendString) {
    initialString &= appendString;
}

append(myString, " shaffer");
// myString now holds "jesse shaffer"
<!--- tag version --->
<:function>
    <:argument name="initialString" type="string" required="true" byReference="true" />
    ...
</:function>
1 Like

SInce strings are immutable in Java, this stands for CFML & Lucee too.

Although this can be overcome with some workarounds, it will be a very nasty hack with an awful performance.

BTW, I always wanted to be able to do this:

var myList = "foo";
ListAppend(myList, "bar");

But I can’t. I have to do this:

myList = ListAppend(myList, "bar");

Because this type of list is just a sting. And strings are immutable. And there are many good reasons they are.

I raised this previously for Railo: https://issues.jboss.org/browse/RAILO-3209

Micha - quite reasonably - rejected the notion and invalid.


Adam

That is fair enough - the only thing I would say is that this already works though:

str = "bl";
str &= "ah";

str == "blah";

My guess is what happens in this operation is that the &= operator replaces the “str” key in whatever scope it lives in with the concatenated result.

That is actually more along the lines what I want:

a = null;

function makeItOne(&ref) {
    // internally, ref is a special object that holds the scope and key of the original variable passed in.  any operations done on ref are passed through to that scope for that key...
    ref = 1;
}

// a == null;
makeItOne(a);
// a == 1

But then, in the instance of a literal:

makeItOne("blah");

it would pass by value.

One thing to note here is the issue really isn’t one of references, it’s that all string operations return a new String. There is no way to alter a String once it’s been created. So even if you could specifically pass the String by reference (which as Micha said in that link I gave you, it kinda is. Kinda), it does you no good cos as soon as you perform an operation on that String, you get a new String (with a new reference) back.

If you wanted to do what you are trying to do here, you’d use a StringBuilder instead, because that doesn’t have the (purposeful) “limitations” that Strings have.

So perhaps is the question “would it be handy if it was easier to create a StringBuilder instead of a String?”?


Adam

I think you’ve missed my point, for my own poor explanation. I did read the issue, and understand. This is beyond strings and how they work - I understand their immutability. What I want is not to be able to make strings mutable, I want to replace the original variable, it its original scope with whatever I want.

// example of how I might do this with an reference component:

component Reference {
    property scope;
    property key;

    public any function getValue() {
        return scope[key];
    }
    public Reference function setValue(any value) {
        scope[key] = value;
        return this;
    }
}

function replaceWithArray(Reference ref) {
    ref.setValue([1,2,3,4,5]);
}


var notAString = 12345;
replaceWithArray(new Reference(scope=local,key="notAString")); 
dump(notAString); // dumps an array instead of a number

Now, internalize that to the engine, so that this is essentially equivalent to the above:



function replaceWithArray(&ref) {
    ref = [1,2,3,4,5];
}

notAString = 12345;
replaceWithArray(notAString); 
dump(notAString); // dumps an array instead of a number

Right. So actual pass-by-reference, not the pass-by-reference-copy that Java does.

I’m not sure something running on Java would be able to do that, would it? Java simply doesn’t expose the necessary information, I think?

But, yeah, I’ve had the need for this in the past. But seldom.

Cheers for the clarification.


Adam

Now, that’s an interesting idea. An easy, non-having-to-write-Java-code to create a StringBuilder.

However, one would have to be really careful doing that for performance and memory management reasons, I suppose. If that was widely used, you could end up with issues in those areas as there’s a good reason that the JVM has the String Constant Pool.

I’d be really interested to see an actual use case of such a behaviour. @adam_cameron, @dajester2013 - keen on sharing a situation in which you could really make good use of that?

I’m buggered if I can remember, Kai. Obviously I couldn’t do it, so I needed to work around it some other way.Also, for all I know the approach I was hoping to take with pass-by-reference might not have been the best approach anyhow, as is often the case with the first iteration of the way I decide to do things.

And mate, do me a favour? Include some context in your responses? It makes it easier to work out what - specifically - you’re talking about when I’m reading the email. Cheers.


Adam

Oh look - I just learned to quote in here :smile:

K

Discourse is the way forward mate. Honest.

##Passed by value? No!##
In Lucee all variables are passed by reference always (also in ACF BTW), simply because there is no other way to do it.
Java is supporting primitive types (char,int,boolean,short,long,double,byte) BUT this types are not used in Lucee, simply because we store all data in maps (java.util.Map) and maps only can store Objects.
So if you use for example a number in Lucee, you are using an object from the class java.lang.Double what is a wrapper class for the primitive type “double”.

This example will show you:

nbr=123;
bool=true;
str="Susi Sorglos";

function meta(o){
   dump(getMetaData(o).name);
   dump(o.hashCode());
}

meta(str);
dump(str.hashCode());
meta(nbr);
dump(nbr.hashCode());
meta(bool);
dump(bool.hashCode());

executing that example gives you an output like this:

java.lang.String
-19311561
-19311561
java.lang.Double
1079951360
1079951360
java.lang.Boolean
number	1231
number	1231

You can see the classes of the objects and the hashcode of the objects, hashcode are unique for every instance, so you clearly see that you have the same object inside the function as you have outside.

So objects are passed by reference always!

##But what about …##

###“passby” with arguments###
You can use the argument “passby” with cfargument (hidden feature in Lucee for backward compatibility for older applications, for example “transfer”)

<cffunction name="test">
<cfargument name="arg" passby="value">

this does not really pass by value, it simply clones the object before passing by reference, so it is the same as you would do the following

test(duplicate(whatever));

##ACF is passing arrays by value##
The same as above it only simulates this, in fact they are cloned and pass by reference.
Again passing objects by value is impossible in the JVM.

##Immutable Objects##

As @kliakos already explained the following classes are immutable (java.lang… String,Long,Short,Integer,Character,Boolean,Integer …), so if you do the following code

str = "bl";
dump(var:str.hashCode(),label:"hash before"); 
str &= "ah";
dump(var:str.hashCode(),label:"hash after"); 
dump(str == "blah"); // equal operator
dump(str === "blah"); // identical operator

output:


hash before - 3146
hash after - 3026417
true
false

You do not manipulate an existing string, it is producing a new string, as you can see in the hash dump.

##StringBuilder/StringBuffer##
So the point is not that objects are passed by value, problem is that they are immutable, in java we use the class java.lang.StringBuilder or java.lang.StringBuffer (thread safe) to manipulate Strings.
of course you can also use them in CFML as following

createObject("java","java.lang.StringBuilder").init("Susi ").append("Sorglos");

But speed wise this is not worth doing it, because the overhead you have with reflection is more than what you win by avoiding to create new objects.
Lucee is well aware of StringBu… objects and can handle them the SAME way as String, so you can do things like this:

1: sb = createObject("java","java.lang.StringBuilder").init("Susi");
2: dump(sb);
3: dump(sb.hashCode());
4: sb&=" Sorglos";
5: dump(sb);
6: dump(sb.hashCode());

but this is still producing a new string on line 4, we was already considering to append " Sorglos" in this case to the existing object, but this would break backward compatiblity (also to ACF).
But we could think about it for the Lucee dialect.

1 Like

small addition, we could of course think about adding support for a syntax like this

sb=@"Susi Sorglos";

shorthand for

sb=createObject('java','java.lang.StringBuilder').init("Susi Sorglos");

You’re missing the point of the conversation, Micha. It depends on which vernacular one uses as to whether Java is pass by reference or pass by value. The value being: that of the original reference. But it’s a different reference. Generally “pass by reference” implies “actually the same reference”. This is something Java cannot do (to the best of my knowledge, which - I admit - is purely research-based, not practical-based).

For the purposes of this conversation, Java does not “pass by reference” in the way you’re using the term.

I demonstrate the difference towards the bottom of this article: “Complex data-types in CF, and how they’re not copied by reference / One last thing”, and there’s more discussion here: “Java is always pass-by-value…” and here “Java is Pass-by-Value, Dammit!” (and any number of other places).

I’m not contending there are two different meanings to the notion of “pass by reference”, and both are equally valid in the appropriate context. But it’s only helpful if we agree to using the same one, in the scope of a conversation about the concept.


Adam

IMO, I wonder whether that really gaining much other than language bloat, when one can just use StringBuilders directly anyhow? Also in the context of my previous reply, It’s not really addressing the issue at hand anyhow, is it? Perhaps something to discuss in another thread (one about mutable strings, rather than passing by reference).


Adam

Yeah maybe, but then the title of the thread is wrong, then this has nothing to do with “simple values”, it makes no difference if you pass simple or complex values.

so what we talking about is that when you do:

sct.a=new MyComponent();
sct.b=&sct.a;
sct.b=new MyOtherComponent();

that in this case also “a” should points to “MyOtherComponent”

This has nothing to do with Java, we could do this, Lucee already does this internally for the “for” and the “loop” statements.
So it has not to search for the index/item variable each round.

Indeed. Over the course of the conversation, the topic under discussion kinda clarified & changed a bit.

Yup, that’s it.

Nice one. I think some concrete and common (or at least useful) real-world use cases might still be in order before deciding this is a good use of anyone’s time?

(I still cannot remember why I wanted to be able to do this, previously)


Adam

Yes, it is - I’ve changed it. It took me a bit to clarify exactly what I was talking about.