Sign in to follow this  
WitchLord

Opinion: AngelScript 2.0 operators

Recommended Posts

WitchLord    4677
Hi, everyone! As I'm working on AngelScript 2.0 I'm finding it necessary to take certain decisions, some are easier than others. Right now I've come upon one that is not so easy to take on my own. With AS 2.0 every object is stored by reference (sort of like a smart-pointer). Thus if you assign an object variable to another both variables will hold references to the same object. My doubt is what I should do with the operators for objects. Alternative 1 will have operators for assigning and comparing object references, but will not allow overloading of operators to make it easier to use objects in expressions. Advantages: Simpler language hence smaller library, and easier interface. Disadvantages: Clumsier to write code with many operations on objects. Function calls instead of operators.
// Declare object variable
obj a = new obj();  

// assign reference 
b = a; 

// copy object value
b.Copy(a);

// comparison of the reference
a == b; 

// Add two objects
a.Add(b);  

// Compare the value of the objects
a.Compare(b); 

Alternative 2 will have special operators for assigning and comparing references, and will allow overloading of operators for normal objects. Advantages: Cleaner syntax where objects are included in expressions. Disadvantages: More complex language, and more complex library and interface.
// Declare object variable
obj a := new obj();  

// assign reference 
b := a; 

// copy object value
b = a;

// comparison of the reference
a === b; 

// Add two objects
a + b;  

// Compare the value of the objects
a == b; 

I also have a third alternative. Which would allow the application to register primitive objects. Primitive objects are classes that only hold primitive values, i.e. it doesn't need a destructor or special assignment operators to manage resources. These primitive object could be stored by value instead of by reference, just like normal primitives like int, float, etc. The library could allow operator overloading only for this type of object. It would then be easy to write special datatypes like Vector3, Point2D, that allows use of operators in expressions. Other objects that need the special management are passed around by reference and don't allow overloaded operators. Advantages: Allows the creation of new primitive types in the language, while still allowing the use of references. The language will still be easy to use. Disadvantages: More complex library. Let me hear what you think. And if you have suggestions for other alternatives I'm also interested in hearing those. Personally I'm leaning towards alternative 2.

Share this post


Link to post
Share on other sites
WitchLord    4677
Giving it some more thought I came up with a 4th alternative:

Normal binary and unary operators will be overloadable, but the assignment operator (including compound assignment) is not overloadable.


// The object has the + operator overloaded

// Declare object variable
obj a = new obj();

// assign reference
b = a;

// copy object value
b = new obj(a);
b = a.clone();
b.copy(a);

// comparison of the reference
a === b;
a !== b;

// Add two objects
a + b;

// Compare the value of the objects
a == b;
a != b;




A compound assignment would unlike C++ be only a faster way of writing the operator followed by the assignment, i.e:

a += b;

is the same as

a = a + b;

(In C++ this is not necessarily true as += is a special operator)

I believe this will be the way I'll implement AS 2.0, unless you find any arguments that makes me change my mind.

Share this post


Link to post
Share on other sites
twanvl    512
I would prefer alternative 2, but I find := not a really logical choice for the 'assign reference operator', maybe '= &' like in PHP? You could have a 'make a reference' operator that returns a reference to an object, then you can assign that reference to a variable.

EDIT: I have always found it very confusing that = means copy assignment for primitives, and reference assignment for objects in for example Java. If = becomes assign reference; you should not make an exception for buildin primitive types.

Share this post


Link to post
Share on other sites
Licu    128
I would prefer to be as close to C++ as possible. IMO usage of references to objects is okay as long as it doesn't add new strange operators to the language, like === or := (Pascal :( style). Many of us choose AngelScript because it was close to C++ (now is fast too :)

So for objects I would suggest going with references as default in operators and when values are needed the object name could be used (for one of the members):

Obj a = new Obj(), b;
// Reference assigment
b = a;
// Value assigment
b = Obj(a); // or Obj(b) = Obj(a)
// Reference comparison
b != a
// Value comparison
Obj(b) != Obj(a)

Overloading of operators are only for value operators (why should I overload a reference comparison ?).

Another alternative would be usage of &:

// Reference assigment
&b = &a;
// Value assigment (error if b is not pointing to a valid object)
b = a;
// Reference comparison
&b != &a
// Value comparison
b != a

Or maybe * to dereference:

// Reference assigment
b = a;
// Value assigment (error if b is not pointing to a valid object)
*b = *a;
// Reference comparison
b != a
// Value comparison
*b != *a

If this is not possible (maybe would complicate the syntax as pointers do) I would go with alternative 1 which is closer to C++ syntax.

Share this post


Link to post
Share on other sites
desertcube    514
Personally, I think that passing by references should be "behind the scenes" and as transparent as possible. Therefore, when you assign a = b, a should be a copy of b, so that modifying b will not affect a.

Here's what I think should happen (also note that I don't like the use of new, it confuses the hell outof me in c# how you can use new on some objects, but some of them don't need it!?)


// Declare object variable
obj a();

// assign/copy object
b = a;

//b.ModifyValue();
//now a != b!

// make them point to the same thing
b = &a;

// comparison of the two references
a.Is(b);
//a is b; //Looks nicer, but harder to implement
//usage:
//if (a is b)

// Add two objects
a + b;

// Compare the value of the objects
a == b;




Note, to make the two object point to the same thing (so modifying b will change a), i used &object as I believe it looks like c++ and forces you to think, do I want both object pointing to the same thing? I don't think the & operator should return anything, it should just be used by the compiler.

Just though I'd give you some more ideas.

Share this post


Link to post
Share on other sites
Mortal    132
IMO, if you're going to be adapting the way Java does objects, it would make more sense to follow Java's syntax instead of ignoring the time Sun spent on designing it.

Though I think keeping with the C++ syntax of using & to assign references then letting = be overloadable would be best, I'm guessing parsing the & would add to the compile time.

Otherwise, I like alternative 4 the most, aside from the === and !==. Perhaps something like ref(a) == ref(b) would work if & couldn't be used. I feel it depends a lot on how often people will need to use the reference of an object in comparison tests.

I've probably just said I like all the different ways, but what I mean is that if keeping the language as much like C++ as possible and be more familiar with C++ coders is higher priority, then go with that route, but I personally would prefer to code using alternative #4.

Just my two cents.

Share this post


Link to post
Share on other sites
Gyrbo    187
Right now, I think Licu's first idea is the cleanest. I do like the syntax of the second better, though.

desertcube also has some nice ideas with the "is" operator. To the parser, it shouldn't really matter if it uses == or is. I recall that the binary operators were first in words (xor, or, not, ...).

I personally think that assignment should make a copy by default, as this is the most logical reason for using that operator. I can't really think of any instances where you wouldn't want to make a copy. So in short, operators should reffer to the value by default. The refference should be used if a special operator is used, like &.

I'm really against using special operators like := and ===, they confuse the heck out of me.

Share this post


Link to post
Share on other sites
Deyja    920
I know what's going to happen to me. I'll become helplessly lost, and generate tons of bugs, wondering 'I set a to b... if I change a, won't b change?? Oh spat!'.

But I've already decided I won't be going to AS2.0 for this project so :P

Share this post


Link to post
Share on other sites
Mortal    132
In response to Gyrbo, the reason you'd want to assign things by reference is speed. You don't want to copy objects all the time, that's why C++ has pointers. In Java, they do it by having everything be references.

Say you're going through some array, like complexarray[a][b][c], and want to start calling stuff from an object in it, so you do Object temp = complexarray[a][b][c]; If temp was just a copy or clone, modifying it would mean nothing - complexarray[a][b][c] would remain unmodified. The solution in C++ is to do Object *temp = &(complexarray[a][b][c]); or similar (I forget order of op. with &).

I believe AngelScript 1 already does this the C++ way, but WitchLord wanted to have 2.0 to be similar to Java where the variables are references (At least that's what I understand) and so it would make little sense to have the assignment operator do cloning.

Hopefully that makes some sense.

Share this post


Link to post
Share on other sites
Gyrbo    187
Mortal, thanks for explaining that. I already understood the concept of refferences, but I only used them for functions before.

I still think that copy by default is the most desirable. If you make a copy instead of a refference, there will be less cases of error. Otherwise you might have people wondering why changing a also modifies b.

I don't have a java background, so I'm basing myself on PHP here. I like the refference system they use (in PHP 4 at least, I heard it changed in version 5). You simply use the & operator if you want to create a refference, both in functions and in assignment.

I'm not sure if this is feasable(sp?), but having a special refference type might be useful if copying is the default behaviour. The reason this might not get adding is because this looks a lot like the pointers WitchLord was trying to remove.

Share this post


Link to post
Share on other sites
WitchLord    4677
I'll have to agree with that having the assignment operator do a reference assignment by default can be confusing. As I want the script language to be as easy to use as possible, I'll avoid that.

Still at times it is good to have a reference assignment. Maybe I'll go with:

a = &b; // a references b

or perhaps simply use

obj &a = b; // Initialization of reference, further assignment affects b.

I also agree with that the fact that objects are stored by reference should be transparent.

I would also prefer not to add new operators to the language, as it is already quite full of them. I will have to add an operator for comparing references though, or maybe a built in function like C++'s sizeof().

Thanks for the feedback everyone. It's been really helpful. If you have some more suggestions or ideas, please let me know.

---------

I feel I need to explain some of my decisions as well.

I decided to remove pointers from AS2.0 because they complicated matters, and since the language didn't allow manipulation of the pointers anyway, they weren't all that useful.

I also decided to store all objects by reference in the script engine, because it allows me to identify the type at run-time, which makes it a lot easier to do exception handling and context serialization, etc. It should also be quite easy to do dynamic casting of objects with inheritance using this model.

AngelScript won't be exactly like C++, but I'll try to keep as close to it as possible.

Share this post


Link to post
Share on other sites
Gyrbo    187
I think that you should support both methods of refference assignment, but that's just my opinion.

As for comparing references, you should make that as transparant as possible. Some things that I feel are acceptable are:

a == &b
&a == &b
a is b
isref(a, b) //or other name



Some things that I would preffer not to use:

a === b
a.isref(b)
a &== b //Could work, but only if you really don't have anything else
a =&= b //some variation of the above
a !&! b


Share this post


Link to post
Share on other sites
Mortal    132
I definitely agree with Gyrbo, on both accounts. I prefer the first type of reference assignments over the second, but having both shouldn't be too much trouble I would think.

Some thoughts,
a == &b looks like it would be an overloaded bool operator==(Object * obj), implemented in the class that a belongs to, not dealing with a's reference.
&a == &b is a lot more natural for C++ programmers, and I feel the best way to compare references since it reads 'reference a has the same numeric value as reference b' or close enough :P
I also like a is b, though with this it'd make sense to also provide 'a is <class>' that checks if a is a subtype of <class>.
isref(a,b) I would honestly put in the second list of don't do's :P But that's just me

Share this post


Link to post
Share on other sites
jetro    144
Generally I like how Java implements things.. in this case, when you do "a = b;" it's reference assignment, and a real copy is done by other means, e.g. via a method (for new copies there's the clone-method). Comparison is through a method as well - "a.equals(b);"

When everything is done via references I think that's pretty clean way to solve the problem. It'll still require user to know and understand that they are working with references. But the other ways to do it, e.g. making "=" to a real copy operator, having "===" for reference comparison etc. sound to me like they really aren't easier to understand at all.

Since AS is heavily C++ influenced, and becoming a bit more Java influenced as well, my personal opinion is that I'd like it to implement things in Java way when possible, and if not, then do it like C++ does it. (that is, avoid adding custom operators with syntax like ":=", "===" etc)

Share this post


Link to post
Share on other sites
Sly    128
I agree with jetro. Make it clean and clear to the user what they are doing. The different obscure operators that I have seen suggested in this thread are just going to cause issues through confusion, all because people want to type the least number of keys possible to get something done.

b = a; // Both reference same object instance
b.Copy(a); // b is a separate identical object instance to a

So I would strongly endorse alternative #1.

Share this post


Link to post
Share on other sites
WitchLord    4677
OK. It seems that everyone agrees that no new operators should be added if at all possible. I agree with that.

What still remains is if an assignment should create a copy of the object, or simply a new reference to the object. There seems to be different opinions here.

Having the assignment do a reference assignment by default would be the same as if all your C++ objects were stored by pointers, so this is in fact very similar to how C++ does it as well. The difference is that the pointer is an implicit smart pointer, that will make sure that the object is freed once all references to it are released.

It may be a little less confusing to a script writer when reference assignments are done. But I think that is only if he didn't read the manual for the script language. On the other hand I believe almost all scripting languages that deal with objects are actually doing reference assignments, think javascript, VBScript, Lua, etc. Why should AngelScript be different? Besides, a reference assignment is a lot faster than making a copy of the object.

Regardless of the decision taken on the question of assignments. AngelScript will continue to support overloading of operators like +, -, etc, as I believe it make the code much easier to read. I believe the comparison operators should also be overloadable operators, comparing the value of the objects and not the reference.

I'm currently leaning towards doing reference assignments. A ref comparison would then be made with the 'is' operator (it's a new operator, I know, but at least it is not obscure [wink]).

Share this post


Link to post
Share on other sites
desertcube    514
Quote:
Original post by WitchLord
Having the assignment do a reference assignment by default would be the same as if all your C++ objects were stored by pointers, so this is in fact very similar to how C++ does it as well. The difference is that the pointer is an implicit smart pointer, that will make sure that the object is freed once all references to it are released.


That statement is very true, but I thought that the use of references was ment to be a "behind the scenes" change i.e. the script writer should more or less be unaware of the change.

The only argument that I have is when exactly in C++ do you actually do this:


int a = 10;
int & b = a;
b = 20; //Now a == 20

RandomClass myClass;
int & c = myClass.subclass.member;





The last case is about the only time I would personally use a reference; not just for performance, but for ease and readability. However, most the time I do assignments it's because I want to chnage the value. E.g:


//start is an int passed into the function
for (int current = start; current < end; ++current)
//access some array

if (start == current) //we didn't move through the array, perhaps some condition was met and it terminated early
//Do stuff

int offset = current - start; //Now wtf happens?





Now what happens in the above code? Using references, everytime current is advanced, it will advance start!? So the final if statement will always be true! Also, what happens in the last line, will offset be a regular int, or will it be a reference to a random bit of memory?

Admitadly, this is a silly example, but you get the idea.

BTW, I see you like my 'is' operator idea [embarrass] But by no new operators, do you mean that literally i.e. you wont be doing:


int a = new int;

Share this post


Link to post
Share on other sites
Gyrbo    187
I've already voice my opinion, but I like doing so, so I'm doing it again :p.

I have to agree with desertcube here. Most of the time when you do assignment, you actually need the new variable to contain a copy. I personally highly dislike something like Class newvar = oldvar.Copy(), especially when you have to do it a lot. Class newvar = *oldvar; is still acceptable IMHO, but since you probably use it most often, it should be the default. (See desertcube's example)
Furthermore, it would be unlogical IMO to have all operators except the assignment work on the value. Even new += old would change the value, whereas new = new + old wouldn't.
The only instance when assignment would default to reference assignment would be if the new variable is explicitly declared as reference.

About new operators: I don't really mind them as long as they're logical and unambiguous. Textual operators are usually clearer than symbolic ones.

Share this post


Link to post
Share on other sites
WitchLord    4677
Reference variables to primitive types won't be allowed, as it would allow a function to store a pointer to some memory that could later be freed, making the reference variable point to illegal memory.

I've already implemented AS 2.0 to do assignment by reference because it was easier, but this doesn't mean it is set in stone. I will leave it like this for WIP 1, because I want to get everything back up and running soon. Afterwards I'll take the decision if I will convert it to do assignment by copy.

I see no problem with having all operators except assignment work with the value. An addition takes two objects, computes their sum, and returns a new object with the result. a += b will just be a shorter way of writing a = a + b; It will not be a separate operator as it is in C++.

The 'new' operator will work like it does in C++. It will allocate an object and return it already initialized. Object a = new Object();



Share this post


Link to post
Share on other sites
desertcube    514
Quote:
Original post by WitchLord
The 'new' operator will work like it does in C++. It will allocate an object and return it already initialized. Object a = new Object();


Fair enoiugh about the reference thingy, but I'm still confused about the need for the 'new' operator. To declare a primative, such as an int, I just do this:

int myInt;

But, if I want to create a custom object, I have to do this:

SomeClass myClass = new SomeClass();

I'm not sure why I need to do this though! What is the purpose of the 'new' operator? Surley it would be nicer to do this:

SomeClass myClass(); //Create an object
SomeCladd myClass; //Same as the above, the default constructor is called

The point I'm trying to make is I'm not sure angelscript needs the operator 'new', as everything you create will infact be a smart pointer, why complicate things. Perhaps I'm missing something, but I don't like the way in C# you have to use new unless it's a primative or struct (in C# there is a difference between classes and structs), but if it is a struct, you can still use new if you want. Since C# has garbage collection (AS kinda does, as the scri[pt takes care of all the memory), I've never really understood the need to declare somethings with new, but otherthings don't need it.

I hope that makes sense, my question is basically why do we need operator 'new'?

Thanks for your time.

Share this post


Link to post
Share on other sites
Mortal    132
The main difference is that int is a primitive. It isn't an object at all. Even Java does it this way - int does not inherit from Object, but they do have Integer, Double, String, etc.

That is why doing assignment with int, or any primitive, isn't by reference.

Object o; just creates a null reference. Object o(); isn't supported, because then o wouldn't be a reference, but an object itself. Object o = new Object(); however is because o is then a reference to a newly allocated Object.

This is where Java differs from C++, because C++ supports pointers, so you can do Object *o = new Object(); as well as supporting pure objects, like Object o(); But to remove pointers, I believe you would also need to remove pure objects (Or whatever you'd like to call them).

Share this post


Link to post
Share on other sites
Gyrbo    187
When looking as AS from a C++ perspective, I would say using new allocated it on the heap (persistent object). Not using the new allocates it on they stack, which means that it gets deleted as it goes out of scope. I'm not sure if that's how it's going to be, though.

AS currently supports creating objects without the new operator, so I don't see a reason why it should be removed.

AS doesn't do garbage collection (AFAIK), only reference counting. I'm not sure if it's even possible to do full garbage collection because of the ineraction between AS and C++. What would happen if you make AS allocate an object and use it in a C++ function? The other way around would also cause problems because the gargabe collector doesn't know about it.

Share this post


Link to post
Share on other sites
desertcube    514
First of all, thanks for explaining that to me Mortal.

However, you also illustrated what I've (indirectly) been trying to say. Sorry to keep going on about it, but the use of references, IMHO, should be a change in the actual engine, not the scripts. I believe that the scriptwriter shouldn't have to worry about the physical storage of any object, whether primitive or custom.

I therefore think that we don't need operator new; the script should handle the creation of the object for us. If I am the only one who thinks like this, I will gladly shut up, but I'm not sure I've expressed my opinion clearly enough.

Share this post


Link to post
Share on other sites
Mortal    132
Quote:
Original post by desertcube
First of all, thanks for explaining that to me Mortal.

However, you also illustrated what I've (indirectly) been trying to say. Sorry to keep going on about it, but the use of references, IMHO, should be a change in the actual engine, not the scripts. I believe that the scriptwriter shouldn't have to worry about the physical storage of any object, whether primitive or custom.

I therefore think that we don't need operator new; the script should handle the creation of the object for us. If I am the only one who thinks like this, I will gladly shut up, but I'm not sure I've expressed my opinion clearly enough.


Nah, it's just that (As I have read) the major change in AngelScript 2.0 is that pointers are being removed from the scripting language itself, not the library code, because Witch finds them too difficult/messy/complicated to deal with. With the removing of pointers from the script language, other things have to be changed too.

Share this post


Link to post
Share on other sites
Lbas    122
I would personnaly prefer keep in mind C++ style, so assignement would still make copy of value, and not reference (I think it's clearer when references are specified using '&' to avoid dangerous confusion like the desertcube's example shows).

BTW, as you (Wichlord) said that operators would still be overridable, wouldn't be an idea to find, which would allow us (as developpers, not scriptwriters) to choose between the 2 ways (like StringFactory() exist for strings management) (with c++ way switched on by default :)). It could be provided either as defines, or operators factory, or whatever you think is better...

Lbas

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this