Operators?
Table of Contents
Operators are an integral part of modern programming, Concurnas dedicates much of its syntax to supporting operators. Most of these operators are 'universal' and widely used outside of conventional programming, such as those concerning arithmetic and basic mathematics, some are more specific to programming in general such as bit shift related operators and some are unique to modern programming languages such as the null safety related operators.
Concurnas also dedicates much support to operator overloading, this helps facilitate the construction of domain specific languages within Concurnas.
Supported Operators?
Concurnas supports the following extensive set of operators:
Class |
Operator |
Token |
Description |
Example |
Input types |
Return type |
Associativity |
---|---|---|---|---|---|---|---|
Sizeof |
Sizeof |
sizeof |
Calculate the off heap size of the operand |
sizeof "myString" => 128 |
Object |
Long |
Left to right |
Contains |
In |
in, not in |
Check if the left hand operand is in the right hand operand |
1 in [1 2 3 4 5] => true |
Any Array or Collection of Component type |
Boolean |
Left to right |
Not |
Not |
not |
Flips boolean value of right hand operand |
not true => false |
Boolean |
Boolean |
Right to left |
Postfix |
Increment |
++ |
Increments operand by 1 |
expr++ |
Integral |
Integral |
Left to right |
Decremement |
-- |
Decrements operand by 1 |
expr-- |
Integral |
Integral |
Left to right |
|
Prefix |
Increment |
++ |
Increments operand by 1 |
++epxr |
Integral |
Integral |
Right to left |
Decremement |
-- |
Decrements operand by 1 |
--expr |
Integral |
Integral |
Right to left |
|
Positive |
+ |
Positive value of operand |
+10 => 10 |
Integral |
Integral |
Right to left |
|
Negative |
- |
Negative value of operand |
-10 => -10 |
Integral |
Integral |
Right to left |
|
Multiplicative |
Multiplication |
* |
Multiplies left and right hand side operands |
2*2 => 4 |
Integral |
Integral |
Left to right |
Power |
** |
Raises left operand to the power of right operand |
3**2 => 9 |
Integral |
Integral |
Right to left |
|
Division |
/ |
Divides left hand operand by right hand operand |
10/3 => 3 |
Integral |
Integral |
Left to right |
|
Modulus |
mod |
The remainder of the division operator applied to the operands as above |
10 mod 3 => 1 |
Integral |
Integral |
Left to right |
|
Additive |
Addition |
+ |
Adds the left and right hand side operands |
2 + 2 => 4, "hi" + "there" => "hi there" |
Integral or String |
Integral or String |
Left to right |
Subtraction |
- |
Subtracts the right hand operand from the left hand operand |
2 - 4 => -2 |
Integral |
Integral |
Left to right |
|
Bitshift |
Left shift |
<< |
The left operands value is moved left by the number of bits specified by the right operand. |
0b001 << 2 => 4 (0b100) |
Integral |
Integral |
Left to right |
Right Shift |
>> |
The left operands value is moved right by the number of bits specified by the right operand. |
0b100 >> 2 => 7 (0b111) |
Integral |
Integral |
Left to right |
|
Unsigned right shift |
>>> |
As above but shifted values are filled with zeros |
0b100 >> 2 => 1 (0b001) |
Integral |
Integral |
Left to right |
|
Complement |
comp |
Complement of a single expression |
comp 0b0001 => 14 (0b1110) |
Integral |
Integral |
Right to left |
|
Relational |
Less than |
< |
Check if the left hand operand is less than the right hand operand |
2 < 4 => true |
Integral |
Integral |
Left to right |
Greater than |
> |
Check if the left hand operand is greater than the right hand operand |
2 > 4 => false |
Integral |
Integral |
Left to right |
|
Less than or equal |
<== |
Check if the left hand operand is less than or equal to the right hand operand |
2 <== 2 => true |
Integral |
Integral |
Left to right |
|
Greater than or equal |
>== |
Check if the left hand operand is greater than or equal to the right hand operand |
2 >== 2 => true |
Integral |
Integral |
Left to right |
|
Instance of |
Instance of |
is, isnot, is not |
Check if the left handoperand is (or is not) a subtype of the right hand type operand |
expr is not Object => false |
Object, Type |
Boolean |
Left to right |
Cast |
Cast |
as |
Cast left operand to be type of right hand type operand |
expr as MyClass => expr is now of type MyClass |
Object, Type |
Object |
Left to right |
Equality |
Structurally equal |
== |
Check if the value of the left and right hand operands are equal |
10 == 10 => true |
Any |
Boolean |
Left to right |
Structurally not equal |
<> |
Check if the value of the left and right hand operands are not equal |
10 <> 9 => true |
Any |
Boolean |
Left to right |
|
Referential equality |
&== |
Check if the object on the left and right hand operands are the same |
Integer(1) &== Integer(1) => false |
Any |
Boolean |
Left to right |
|
Referentially not equal |
&<> |
Check if the object on the left and right hand operands are not the same |
Integer(1) &<> Integer(1) => true |
Any |
Boolean |
Left to right |
|
Bitwise |
Bitwise and |
band |
Compares each bit of the operands and returns 1 for each if they are both 1 |
0b110 band 0b101 => 0b100 |
Integral |
Integral |
Left to right |
Bitwise or |
bor |
Compares each bit of the operands and returns 1 for each if either equals 1 |
0b110 bor 0b101 => 0b111 |
Integral |
Integral |
Left to right |
|
Bitwise exclusive or |
bxor |
Compares each bit of the operands and returns 1 for each if either but not both equals 1 |
0b110 bxor 0b101 => 0b011 |
Integral |
Integral |
Left to right |
|
Logical and |
and |
and |
Check if the left and right hand operands both resolve to true |
true and false => false |
Boolean |
Boolean |
Left to right |
Logical or |
or |
or |
Check if either the left and right hand operands both resolve to true |
true or false => true |
Boolean |
Boolean |
Left to right |
Ternary |
If expression |
x if y else z |
Return x operand if y operand resolves to true otherwise return z operand |
8 if true else 9 => 8 |
Any, Boolean, Any |
Any |
Left to right |
Invoke |
Invoke |
obj(args?) |
Call the invoke method on object with provided methods |
afunc(); afunc2(1, 'arg2') |
Object |
Any |
Left to right |
Null Safety |
Not null assertion |
?? |
Throws a Null Pointer Exception if left hand side is null, otherwise returns no null type |
maybeNull?? |
Object? |
Object |
Left to right |
Elvis operator1 |
?: |
Returns left hand side if it is not null, otherwise the right hand side expression will be evaluated and returned |
maybeNull?:otherwise |
Object? |
Object |
Left to right |
|
Assignment |
Assignment |
= |
Vanilla assignment |
a = 10 |
Integral |
Integral |
Right to left |
Compound Assignment |
Addition Assign |
+= |
Apply addition operator to left and right operands and set value to left operand |
a += 10 |
Integral or String |
Integral or String |
Right to left |
Subtract Assign |
-= |
As above but subtraction |
a -= 10 |
Integral |
Integral |
Right to left |
|
Mutliply Assign |
*= |
As above but multiplication |
a *= 2 |
Integral |
Integral |
Right to left |
|
Divide Assign |
/= |
As above but division |
a /= 13 |
Integral |
Integral |
Right to left |
|
Power Assign |
**= |
As above but power |
a **= 2 |
Integral |
Integral |
Right to left |
|
Mod Assign |
mod= |
As above but mod |
a mod= 15 |
Integral |
Integral |
Right to left |
|
Bitwise And Assign |
band= |
As above but bitwise and |
a bor= 16 |
Integral |
Integral |
Right to left |
|
Bitwise Or Assign |
bor= |
As above but bitwise or |
a bor= 17 |
Integral |
Integral |
Right to left |
|
Bitwise Xor Assign |
bxor= |
As above but bitwise xor |
a bxor= 17 |
Integral |
Integral |
Right to left |
|
Complement Assign |
comp= |
As above but complement |
a comp= 19 |
Integral |
Integral |
Right to left |
|
Left shift Assign |
<<= |
As above but left shift |
a <<= 20 |
Integral |
Integral |
Right to left |
|
Right Shift Assign |
>>= |
As above but right shift |
a >>= 21 |
Integral |
Integral |
Right to left |
|
Unsigned right shift Assign |
>>>= |
As above but unsigned right shift |
a >>>= 22 |
Integral |
Integral |
Right to left |
The majority of these operators operate upon primitive or boxed primitive types. The integral (or 'numerical') types are defined as: int
, long
, short
, char
and byte
and their equivalent boxed object variants: Integer
, Long
, Short
, Character
and Byte
respectfully.
Sizeof?
The sizeof
operator is designed for working with off heap memory. It provides us an indication of the size in bytes that any object (including arrays) will consume when serialized into a byte format as par the scheme referenced within its optional qualifier. For example, when working on the gpu we can use a qualifier as follows:
leAr = [1 2 3 4 5 6 7 8 9 10]
gpusize = sizeof<gpus.gpusizeof> leAr//size of leAr on the gpu
If no qualifier is provided then the default off heap serialization built into Concurnas is used:
leAr = [1 2 3 4 5 6 7 8 9 10]
gpusize = sizeof leAr//size of leAr as par default scheme
Defining a sizeof qualifier?
If we were to design a customized object serialization scheme, it can be beneficial to implement a sizeof qualifier such that clients of our custom scheme are able to know the size in bytes that objects will consume if serialized via our scheme. This can be easily achieved by defining a function taking a single Object as input and returning a value of type int
. For example:
def sizeofCustomScheme(some Object) int{
//some logic...
return 1//in this example every object is 1 byte large
}
This can then be used via qualification of the sizeof operator as par above.
Contains?
The contains class of operator consists of two variants, in
and not in
. in
can be used with common data collection structures including: lists, sets and arrays to test to see if a value on its left hand side is within the expression of one of the aforementioned data structures on the right hand side. If it's present a boolean
value of true
is returned, otherwise false
. not in
returns true
if the value on the left is not present within the data structure.
alist = [1,2,3,4,5]
within = 2 in alist
in
may be used in a similar way with maps in order to examine whether a key is present within the map. For example:
amap = {2 -> 23, 3 -> 67. 1 -> 55}
within = 2 in amap
The in
keyword has special meaning when used within the context of an iterator style for loop, this is documented in the Iterator style for section.
Supporting contains?
Any class may support the operator in
by providing a contains method, this is discussed in more detail in the operator overloading section.
Not?
The not
keyword simply flips the booleanarity of a value of boolean
type. true
becomes false
and vice versa.
Postfix?
Concurnas supports the usual set of postfix operators one would expect from a modern programming language. Namely the increment ++
and decrement --
operators appearing on the right hand side of an integral expression consisting of a variable, list or array reference. These operators are applied in place. The operators may be used in a place where a value is expected to be returned from it, in which case the previous value of the variable prior to the increment or decrement operator being applied will be returned:
avar = 10
anarray = [1 2 3 4 5]
avar++
anarray[0]--
prevvar = avar++
prevarray = anarray[0]--
Prefix?
The prefix operators behave in a similar manner as the postfix operators though with the addition of the negation: -
operator. Unlike the others, the negation: -
operator is not applied in place and will always return a value. The operators may be used in a place where a value is expected to be returned from it, in which case the value of the variable post operator application will be returned:
avar = 10
anarray = [1 2 3 4 5]
++avar
--anarray[0]
newvar = ++avar
newarray = --anarray[0]
anothervar = 10
negative = -anothervar//negative == -10
Multiplicative?
Concurnas supports the standard set of multiplicative operators which can act upon two, potentially differing, integral types, multiplication: *
, division: /
, power: **
and modulus: mod
. For example:
mul = 3*2 //==6
div = 12/2 //==6
pow = 3**2 //==9
pow = 9**-2 //square root
modu = 10 mod 4 //==2
Attempting to divide by zero with two non floating point (either float
or double
) values will result in an exception of type java.lang.ArithmeticException
being thrown. Attempting to divide by zero with at least one floating point value will result in: Infinity
being returned. 0./0.
will resolve to NaN
.
In situations where the left and right types of the multiplicative operator differ, the more general type shall be used for the return type. For example:
div1 = 13/2f //==6.5 (float)
div2 = 13/2. //==6.5 (double)
div3 = 13f/2. //==6.5 (double)
mulong = 100*10L //1000L (long)
mulong = 100L*10. //1000. (double)
Division and power gotacha?
Often new and even experienced programmers will come across the following sort of problem:
assert 13/2 == 6.5 //fails!
The above fails because 13/2
for integers resolves to 6
and not 6.5
. Perhaps this is the answer desired, but to obtain the 'correct answer' of 6.5
, what we are looking for is a floating point calculation and this can be achieved by casting either of the arguments to the division operator to a floating point type, either float or double:
assert 13./2 == 6.5 //expected asnwer
Additive?
Concurnas supports addition: +
and subtraction -
. These are straightforward and the rules concerning generalization of numerical types as par the multiplicative operators above apply. Examples:
addition = 1+1
subtraction = 1-1
Additionally, the addition operator +
can be used for String concatenation. This is described in more detail in the The String concatenation operator \lstinline!+! section.
Bitshift?
Concurnas supports bit shift operators for integral types. They operate on a bit pattern, given by the left hand operand and a number of positions to shift by the right hand operand.
<<
- Left shift. Shifts a bit pattern to the left.>>>
- Unsigned right shift. Shifts a bit pattern to the right. Shifts a zero to the leftmost position.>>
- Right shift. Shifts a bit pattern to the right. Leftmost bit is shifted contingent on sign extension.
Example:
def shifty(){
a = 17
x1 = a << 2
x2 = a >> 2
x3 = a >>> 2
[x1, x2, x3]
}
shifty() //resolves to => [68, 4, 4]
Relational?
The main relational operators in Concurnas are less than: <
, greater than: >
, less than or equal:<==
and greater than or equal: >==
. These operate upon two integral types. And return a boolean
type. Examples:
lt = 1 < 2
gt = 3 > 2
lteq = 1 <== 2
gteq = 3 ==> 2
Instance of and Cast?
The instance of or cast operators may not be overloaded. They are covered in more detail in theCasting and Checking Types chapter.
Equality?
Concurnas supports two variants of equality, structural equality (as: equal: ==
and not equal: <>
) and referential equality (denoted by prefixing the equality operator with an ampersand: &
). The structural equality operators may operate upon any type, whereas the referential quality operators may only operate upon Objects. For all operator variants, they return a boolean value indicating equality. Let's look at some examples of structural equality:
eq = 10 == 10 //true
eq2 = 10 <> 12 //true
eq3 (1>3) == false //resolves to true
When it comes to structural equality for objects this is achieved by calling the equals method on the left hand side object and passing it the object on the right hand side as an argument. Note that in Concurnas this equality method is automatically created for all Objects and it resolves to provide structural equality, this behaviour can be user overridden to provide different behaviour. More details of this can be found in the Automatically generated equals and hashcode methods section. Examples:
class Person(name String)
d1 = Person('dave')
d2 = Person('dave')
f3 = Person('freddy')
assert d1 == d2 //struturally equal
assert d1 <> f3 //struturally not equal
assert d1 &== d1 //object is referentially equal to itself!
assert d1 &<> d2 //d1 and d2 though structurally equal (above) are not referentially equal
Bitwise operators?
The major bitwise operators take two integral types as input. The following operators are supported:
band
- bitwise andbor
- bitwse orbxor
- bitwise exclusive or
These operators are commonly used in order to apply masks. Example:
def pprint(xx int) => '0x' + String.format("%8s", Integer.toBinaryString((xx+256) % 256)).replace(" ", "0")
value = 0b00010101
bitmask = 0b00000001
pprint(value band bitmask) // -> 0x00000001
pprint(value bor bitmask)// -> 0x00010101
pprint(value bxor bitmask)// -> 0x00010100
Complement?
The comp
operator can be used on an integral type in order to derive the complement of a single expression. Every 0
is flipped to a 1
and vice versa. Example:
orig byte = 0b00000011
complement= comp orig
Integer.toBinaryString((complement+256) % 256) // resolves to: '11111100' - which is the complement of the input byte.
Logical and/or?
Concurnas supports the logical and
and or
operators which can be used in order to chain together boolean
expressions. Naturally, both arguments to these operators must be of boolean
type. More then one and
or or
may be chained together. The operators both return boolean
values. Examples:
orand1 = 2 == 3 or 3>1
orand2 = 2 == 3 or 3>1 or 22>1 or 3 < 9
orand3 = 2>1 and 4>1
Short circuiting?
Short circuiting is a logical optimization in which the second argument to an operator is executed or evaluated only if the first argument does not suffice to determine the value of the expression. This sometimes catches people out when they expect all arguments of an operator to be evaluated.
For a chain of or
operators, evaluated left to right, as soon as one argument evaluates to true
, the value true
is returned (otherwise false
), similarly for the and
operator, evaluated left to right, as soon as an argument evaluates to false
, false
is returned (otherwise true
).
def eval1(toRet boolean) => toRet
def eval2(toRet boolean) => toRet
def toCall(toRet boolean) => toRet
shortcir = eval1(false) or eval1(true) or toCall(true)//toCall will never be called!
shortcir = eval1(true) and eval1(false) and toCall(true)//toCall will never be called!
Ternary?
The Ternary operator x if test else z
can be thought of as a handy shorthand for the slightly more verbose: if(test){ x }else{ z }
(in fact, under the hoot the Ternary operator is translated into this more verbose form). x
and z
must both return a value (of any type) and test
must resolve to a value of boolean
type. Example:
def test() => true
result = 12 if test() else 99
Invoke?
Concurnas allows an invoke
method to be defined for all objects, this permits the following syntax to be used in order to call this invoke
method as an operator, with optional arguments:
class FormatPlus{
def invoke(a int, b int) => "{a} + {b}"
}
fmt = FormatPlus()
fmt(12, 13)//returns: "12 + 13"
This can occasionally be useful tool to use when defining domain specific languages.
Null safety?
The null safety operators are documented in detail in the Null Safety section.
Assignment?
Variable assignment is covered in detail in the Variable assignment chapter. The assignment operator may be overloaded, this is described in the Overloading the assignment operator section, in the case of an overloaded assignment operator being defined for a type, the escaped assign of: \\=
will suppress this behaviour when a value is assigned.
Compound assignment?
Here we concern ourselves with compound assignment applied to a variable which has already been defined. These are: +=
, -=
, *=
, /=
, **=
, mod=
, or=
, and=
, <<=
, >>=
, >>>=
, band=
, bor=
, bxor=
. Taking the addition assignment operator, +=
as an example, the following two statements are functionally identical:
a1 = a2 = 10
a1 += 1
a2 = a2 + 1
assert a1 == a2
That is to say, a1 += 1
is essentially shorthand for: a1 = a1 + 1
.
The compound assignment operators may be applied to array reference operations as follows:
a1 = [1 2 3]
a1[0] += 1
assert a1 == [2 2 3]
Again, a1[0] += 1
is essentially shorthand for: a1[0] = a1[0] + 1
.
Parentheses?
In Concurnas, parentheses can be used or order disambiguate expressions composed of multiple operators. They may also be used in a "no operation" capacity in order to make code easier to read. For example:
orand4 = not (2>1 and 4>1)
Operator Overloading?
Concurnas provides a mechanism by which the aforementioned operators can be supported for object types in addition to the primitives (and boxed versions thereof) above. This functionality can be extremely useful when implementing Domain Specific Languages. The following operators can be overloaded:
Class |
Operator |
Normal Token |
Method name |
Alt Name |
Type overloaded |
Notes |
---|---|---|---|---|---|---|
Assign |
Assignment |
= |
assign |
= |
Left |
Use \= variant of assign to avoid calling overloaded operator |
Unassign |
unassign |
Left |
Where variable of type x implements unassign, use x: to avoid calling unassign operator |
|||
Compound Assign |
Addition Assign |
+= |
plusAssign |
+= |
Left |
|
Negation Assign |
-= |
minusAssign |
-= |
Left |
||
Multiply Assign |
*= |
mulAssign |
*= |
Left |
||
Divide Assign |
/= |
divAssign |
/= |
Left |
||
Power Assign |
**= |
powAssign |
**= |
Left |
||
mod Assign |
mod= |
modAssign |
mod= |
Left |
||
or Assign |
or= |
orAssign |
or= |
Left |
||
and Assign |
and= |
andAssign |
and= |
Left |
||
left shift Assign |
<<= |
leftShiftAssign |
<<= |
Left |
||
right shift Assign |
>>= |
rightShiftAssign |
>>= |
Left |
||
unsigned right shift Assign |
>>>= |
rightShiftU |
>>>= |
Left |
||
bitwise and Assign |
band= |
bandAssign |
band= |
Left |
||
bitwise or Assign |
bor= |
borAssign |
bor= |
Left |
||
bitwise xor Assign |
bxor= |
bxorAssign |
bxor= |
Left |
||
Not |
Not |
not |
not |
Unary |
||
Postfix |
Increment |
++ |
inc |
++ |
Unary |
|
Decremement |
-- |
dec |
-- |
Unary |
||
Prefix |
Increment |
++ |
inc |
++ |
Unary |
|
Decremement |
-- |
dec |
-- |
Unary |
||
Positive |
+ |
plus |
+ |
Unary |
No arguments specified |
|
Negative |
- |
neg |
- |
Unary |
No arguments specified |
|
Complement |
comp |
comp |
Unary |
|||
Multiplicative |
Multiplication |
* |
mul |
* |
Left |
|
Power |
** |
pow |
** |
Left |
||
Division |
/ |
div |
/ |
Left |
||
Modulus |
mod |
mod |
Left |
|||
Additive |
Addition |
+ |
plus |
+ |
Left |
|
Subtraction |
- |
minus |
- |
Left |
||
Bitshift |
Left shift |
<< |
leftShift |
<< |
Left |
|
Right Shift |
>> |
rightShift |
>> |
Left |
||
Unsigned right shift |
>>> |
rightShiftU |
>>> |
Left |
||
Relational |
Less than |
< |
compareTo |
Left |
a.compareTo(b) < 0 |
|
Greater than |
> |
compareTo |
Left |
a.compareTo(b) > 0 |
||
Less than or equal |
<== |
compareTo |
Left |
a.compareTo(b) <== 0 |
||
Greater than or equal |
>== |
compareTo |
Left |
a.compareTo(b) >== 0 |
||
Equality |
Structurally equal |
== |
equals |
Left |
a.equals(b) |
|
Structurally not equal |
<> |
equals |
Left |
not a.equals(b) |
||
Bitwise |
Bitwise and |
band |
band |
Left |
||
Bitwise or |
bor |
or |
Left |
|||
Bitwise exclusive or |
bxor |
bxor |
Left |
|||
Logical and |
and |
and |
and |
Left |
||
Logical or |
or |
or |
or |
Left |
||
Invoke |
Invoke |
a() |
invoke |
Left |
Any number of arguments may be specified |
|
Lists, Arrays, Maps |
Get |
a[y] |
get |
Left |
||
Put |
a[y] = z |
put |
Left |
|||
Sublist |
a[y1 ... y2] |
sub |
Left |
a.sub(y1, y2) |
||
Sublist from |
a[y1 ...] |
subfrom |
Left |
a.subfrom(y1) |
||
Sublist to |
a[... y2] |
subto |
Left |
a.subto(y2) |
||
Put sublist |
a[y1 ... y2] = z |
subAssign |
Left |
a.subAssign(y1, y2, z) |
||
Put sublist from |
a[y1 ...] = z |
subfromAssign |
Left |
a.subfromAssign(y1, z) |
||
Put sublist to |
a[... y2] = z |
subtoAssign |
Left |
a.subtoAssign(y2, z) |
||
In |
in |
contains |
Right |
b.contains(a) |
||
Not in |
not in |
contains |
Right |
not b.contains(a) |
Implementing and using overloaded operators?
Overloaded operators operate upon the left hand element of an expression unless it is unary where there is only one expression the operator is being applied to, or for the int
and not in
operators where the right hand expression must have the relevant method implemented. The left hand element must support the operator being invoked. For non alphanumerical named operators (e.g. +
, **
etc) the raw alphanumerical operator token may be defined as a method name, or it's alternative longhand name may be used (plus
for +
, mul
for *
etc).
class Complex(real double, imag double){
def +(other Complex) => new Complex(this.real + other.real, this.imag + other.imag)
def +(other double) => new Complex(this.real + other, this.imag)
def +=(other Complex) => this.real += other.real; this.imag += other.imag
def +=(other double) => this.real += other
override toString() => "Complex({real}, {imag})"
}
c1 = Complex(2, 3)
c2 = c1@
c3 = c1@
c4 = Complex(3, 4)
result1 = c1 + c4
result2 = c1 + 10.
c2 += c4 //compound plus assignment
c3 += 10.//compound plus assignment
//result1 == Complex(5.0, 7.0)
//result2 == Complex(12.0, 3.0)
//c2 == Complex(5.0, 7.0)
//c3 == Complex(12.0, 3.0)
Above we see the plus and compound plus assignment operator have been overloaded for the object class: Complex. Note that operator overloading methods themselves may be overloaded with differing argument types(just like regular methods).
Operator overloading via extension functions?
It's possible to implement operator overloading via use of extension functions. this is documented in the Extension functions as operator overloaders section.
Overloading the assignment operator?
The assignment operator, =
can be overloaded. Example:
class AssignOPOverload(value int){
def =(a int){ value = a; }
override toString() => "AssignOPOverload: {value}"
}
obj= new AssignOPOverload(100)
obj= 66
//thing == AssignOPOverload: 66
If the overloaded assign method returns a value then this will be ignored. If you wish to suppress invoking the assign operator then using the escaped assign of: \\=
will suppress this behaviour, and will cause the assignment to behave a normal, assigning whatever is on the right hand side to the variable expressed on the left hand side:
class AssignOPOverload(value int){
def =(a AssignOPOverload){ value = a.value + 10000; }
override toString() => "AssignOPOverload: {value}"
}
inst1 = new AssignOPOverload(100)
inst2 = new AssignOPOverload(100)
inst1 = new AssignOPOverload(22)
inst2 \= new AssignOPOverload(22) //bypass overloaded = operator
//inst1 == AssignOPOverload: 10022
//inst2 == AssignOPOverload: 22
Overloading of the assignment operator is used to great effect with off heap memory and gpu memory interaction.
Unassign?
Just as we can overload the assignment operator, so too can be implicitly overload the 'unassignment operator', consisting of a zero argument method named unassign
returning any non void type. This is best illustrated with an example:
class MyUnassignable{
myvar int
def assign(anint int) => myvar = anint;;
def unassign() => myvar
}
inst = MyUnassignable()
inst = 56
res = inst
//res == 56
If we wish to suppress the unassignment operation this can be achieved by using the :
operator as follows:
class MyUnassignable{
myvar int
def assign(anint int) => myvar = anint;;
def unassign() => myvar
override toString() => 'MyUnassignable {myvar}'
}
inst = MyUnassignable()
inst = 56
res1 = ''+ inst
res2 = '' + inst: //suppress calling of unassign()
res3 = inst:tostring() //suppress calling of unassign()
//res1 == '56'
//res2 == 'MyUnassignable 56'
//res3 == 'MyUnassignable 56'
If we wish to create a ref to a variable of type implementing the unassign()
method, and we wish to avoid 'unassigning' it then we must use an extra :
.
class MyUnassignable{
myvar int
def assign(anint int) => myvar = anint;;
def unassign() => myvar
override toString() => 'MyUnassignable {myvar}'
}
inst = MyUnassignable()
inst = 56
ref MyUnassignable: = inst:: //ordinarily we'd just use 'inst:' to create a ref
Overloading of unassignment is used to great effect with lazy variables.
Assignment operators?
If there is an object returned from an overloaded assignment operator which happens to be a ref type, then Concurnas will await for the assignment on the ref type to take place before continuing with execution. similarly, if the operator overload method being invoked is tagged with the @com.concurnas.lang.DeleteOnUnusedReturn
annotation, then delete will be called on the object returned before continuing with execution.
This applies to the following assignment operators:
The assignment operator:
=
The in place assignment operators:
+=, -=, *=, /=, **=, mod=, band=, bor=, bxor=, comp=, <<=, >>=, >>>=
The sublist assignment operators:
[a ... b ] = z, [a ... ] = z, [ ... b ] = z
The escaped assignment operator: \=
is excluded from this behaviour.
Footnotes
1So called as the token looks like the emoticon for Elvis Presley