Operators?

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 and

  • bor - bitwse or

  • bxor - 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