(This is pissing me off, but actually not just me...)
Expression 2 code:
function vector check_ByRef( Input:vector )
{
#local Input = vec()
Input:setX( -Input:x() )
Input:setY( -Input:y() )
Input:setZ( -Input:z() )
return Input
}
local Output = vec( 1, 2, 3 )
print( check_ByRef( Output ) ) # Prints: [1,2,3] UNEXPECTED!
print( Output ) # Prints: [1,2,3] UNEXPECTED!
And just to proof that Vector is "passed-by-reference" with GLua:
local vec = Vector
local VECTOR = FindMetaTable( "Vector" )
VECTOR.getX = function( this ) return this.x end
VECTOR.getY = function( this ) return this.y end
VECTOR.getZ = function( this ) return this.z end
VECTOR.setX = function( this, x ) this.x = x return this end
VECTOR.setY = function( this, y ) this.y = y return this end
VECTOR.setZ = function( this, z ) this.z = z return this end
------------------------------------------------------------
local check_ByRef = function( Input )
--local Input = vec()
Input:setX( -Input:getX() )
Input:setY( -Input:getY() )
Input:setZ( -Input:getZ() )
return Input
end
local Output = vec( 1, 2, 3 )
print( check_ByRef( Output ) )
print( Output )
--[[--------------------------------------------------------
] lua_openscript_cl check-byref.lua
Running script check-byref.lua...
-1.000000 -2.000000 -3.000000
-1.000000 -2.000000 -3.000000
----------------------------------------------------------]]
The results are unexpected to many users, because they assume data-types such as Angle and Vector are classes/objects to be pass-by-ref...
But, as you can see with the above Expression 2 code, it shows that Vector is always "passed-by-value"; it is made wrong and is unwanted behavior, it also results in slower performance! However, there is one exception, the table type seems to be the only type which is "passed-by-reference" in Expression 2. And just to proof that with the following example:
function table check_ByRef_Table( Input:table )
{
#local Input = table()
Input["Key", string] = "New Value"
return Input
}
local Output = table( "Key" = "Value" )
print( check_ByRef_Table( Output )["Key", string] ) # Prints: "New Value" as expected.
print( Output["Key", string] ) # Prints: "New Value" as expected.
... I don't know whether Expression 2 will ever have this implemented correctly (taking into an account how much time have passed since active E2 development by wire devs, and the number of E2s out there). So, me personally won't bother with this though maybe there is somebody who is still and will care enough to create a new operator (or keyword ref) to allow "pass-by-reference" while preserving the current (wrong) behavior, so that it is backward compatible and wouldn't break any of the E2s. Moreover, this is my warning to users/newcomers since wiki lacks many information ...
/sorry-for-bad-english
/end-of-complain
/sign-out
The problem here is that the defined behavior of setX (and co) is not what you think. In E2, it is defined as Returns a new vector where the specified value has been replaced. This has nothing to do with pass by anything.
See https://github.com/wiremod/wire/blob/master/lua/entities/gmod_wire_expression2/core/vector.lua#L373-L386 for more details.
This is also properly explained on the wiki https://github.com/wiremod/wire/wiki/Expression-2#Common_Vector_Commands
Ah, of course array is also pass by reference.
function array check_ByRef_Array( Input:array )
{
#local Input = array()
Input[1, string] = "New Value"
return Input
}
local Output = array( 1 = "Value" )
print( check_ByRef_Array( Output )[1, string] ) # Prints: "New Value" as expected.
print( Output[1, string] ) # Prints: "New Value" as expected.
@Divran Now that you mention that, yeah... But, why close this issue?
Because it's intended behavior, so there is no issue.
What do you think about implementing ref?
I don't believe there are any vector functions in E2 that actively modify the vector itself (EDIT: I checked, there aren't). If we were to implement any, it would be a break from the rule, and would be more confusing, not less.
EDIT: It wouldn't be impossible to do, though, just pointless.
Be precise in what you're requesting.
As @Divran says, it's an invisible implementation detail whether vectors are passed by reference or by value, because no vector is ever modified. Assuming your code was correct according to the V V:setX(n) behaviour mentioned above (ie. it read ... Input = Input:setX( -Input:x() ) ...), would you still expect the 'pass-by-reference' behaviour you're expecting to occur? Because in that instance even the Java equivalent of your code wouldn't give you the behaviour you're expecting.
When you say "implementing ref", do you mean an explicit reference wrapper type similar to C++'s std::ref(T&) or Java's Reference<T>, or a keyword on function parameters similar to C#'s ref, Pascal's VAR and Swift's inout keyword?
The former would be infeasible due to the lack of an expressive type system - ie. we could invent a reference type, but it wouldn't be a reference to a particular type, just to any value in general. It'd act like a singleton array or table, essentially.
The latter I wouldn't be entirely opposed to, but it would need a lot of thought - and given the E2 function system is one of the worst thought out parts of the E2 codebase I imagine the implementation would be seven shades of horrible.
@Divran
I don't believe there are any vector functions in E2 that actively modify the vector itself (EDIT: I checked, there aren't). If we were to implement any, it would be a break from the rule, and would be more confusing, not less.
EDIT: It wouldn't be impossible to do, though, just pointless.
lol. Check again?
I just wanted to tell you, you are very wrong.
Look at this example E2 where I directly modify angle/vector4 object by using their [] (indexer) operator:
local ANG = ang(1, 2, 3)
ANG[2] = 4
print(ANG) # [1,4,3]
local VEC = vec4(4, 5, 6, 7)
VEC[4] = VEC[3] = VEC[2] * 1.5
print(VEC) # [4,5,7.5,7.5]
@Divran @AbigailBuccaneer @TomyLobo How about to reopen this?
(indexer) operator
is not a
vector function
@oldmud0
(indexer) operator
is not a
vector function
Technically, doesn't matter...
Because it doesn't change the fact that [] operator is still an (e2)function:
https://github.com/wiremod/wire/blob/e775ff49b3be94610e6039c2a92dc8f2e35a985e/lua/entities/gmod_wire_expression2/core/vector.lua#L165-L172
@CaptainPRICE feel free to open a new issue where you actually specify what you want - this one confuses multiple different requests, bug reports, and fake news together in one.
Can someone PR a change that tells everyone to fuck off with mutable vectors?
It was hard enough to get rid of them the first time.
@TomyLobo It was added with #411, funny enough, you said it was a good idea. Anyway, this was done back in 2013, so if we got rid of them it'd probably break a few E2s.
I have no idea why I thought that was a good idea back then.
The setPitch/Yaw/Roll functions of the angle type return a fresh instance and so do the vector component setters.
Maybe I didnt read it properly and thought that's what those did as well...
Anyway, it's a bad idea, since it allows spooky action at a distance.
Luckily the output converters limit that effect to E2.
Anyway, it's a bad idea, since it allows spooky action at a distance.
What is that supposed to mean?!
This is useful, so we can avoid creating a new instance(object/table) over and over again. Besides, changing these properties is possible with Angle/Vector (and other) objects in GLua too, usually this can be used to improve performance/speed and actually allows for "pass-by-reference" type of thing in E2 (I will show an example in a moment).
There are also GMod functions which alter the existing object instead of returning a new one (some examples are Vector.Normalize, Angle.SnapTo and many more).
So, yes this is a good thing.
And yes of course, by removing [] (indexer) operators (or modifying how they work at the moment) on those types, it would break existing E2s (for no good reason). I certainly do NOT recommend removing/modifying them in any way.
And here is a small E2 example of angle "pass-by-reference", I've simply ported GLua Angle.SnapTo function to E2:
angle-snapto.txt
What is that supposed to mean?!
"Spooky action at a distance" is a phrase that Einstein used to describe quantum entanglement. What TomyLobo means in this context is that reference parameters allow faraway parts of your code to affect you in ways that aren't obvious, which can be hard to read and debug. The Google C++ Style Guide, for example, bans non-const references because of this.
This is useful, so we can avoid creating a new instance(object/table) over and over again ... usually this can be used to improve performance/speed
This is an implementation detail. It's typically easier for compilers to optimize code when types are pass-by-value and immutable, so by wishing for your own, so we'd miss out on optimization opportunities in future.
Besides, changing these properties is possible with Angle/Vector (and other) objects in GLua too ... There are also GMod functions which alter the existing object instead of returning a new one (some examples are Vector.Normalize, Angle.SnapTo and many more).
E2 and Lua are different languages, and they have different semantics. I don't file bugs against C because its structs aren't pass-by-reference like Java.
And yes of course, by removing [] (indexer) operators (or modifying how they work at the moment) on those types, it would break existing E2s (for no good reason). I certainly do NOT recommend removing/modifying them in any way.
Nobody said anything about that.
And here is a small E2 example of angle "pass-by-reference"
The code you posted would work fine without reference semantics, except for the very last line. See? You're already used to working with value semantics. Embrace it.
Most helpful comment
I have no idea why I thought that was a good idea back then.
The setPitch/Yaw/Roll functions of the angle type return a fresh instance and so do the vector component setters.
Maybe I didnt read it properly and thought that's what those did as well...
Anyway, it's a bad idea, since it allows spooky action at a distance.
Luckily the output converters limit that effect to E2.