Copying Objects?

But the little things they make me so happy

All I want to do is live by the sea

Little things they make me so happy

But it's good it's good it's good to be free

- (It's Good) To Be Free from Definitely Maybe by Oasis (1994)

Concurnas provides a convenient syntax for performing a default deep copy of any Object via the copy operator, the Syntax of which being:

copyOperator: expression @ copyDefinition?

When no copyDefinition is specified the copy operator will provide a deep copy of the expression on the left hand side. E.g.:

class Details(age int=21, firstname String="john", sirname String="doe", friendCount int){
  override toString() => "{(age, firstname, sirname, friendCount)}"

det = new Details(12, "dave", "whatson", 5)
copy = det@

//copy == 12, dave, whatson, 5

Immutable objects, composed of completely immutable objects are not copied since post creation they are unchangeable.

References are not copied since they have their own built mechanism for managing access (from different iso's) - this applies even when an explicit copy operation is performed.

Copy Definitions?

The copy operator provides us with a deep copy of our object structure. Sometimes a deep copy is more than what we need (especially for complex data structures) and it can often be helpful to have control over which fields of an object are copied and how. Copy definitions allow us to control this, the syntax of which is:

copyDefinition: '(' (cdComponent (',' cdComponent)? )? (; ('nodefault'|'unchecked')*)? ')'

cdComponent: fieldName ('=' expression?)

| fieldName (',' fieldName)*

| fieldName @copyDefinition?

| 'super' @ copyDefinition?

Inclusion list?

We can explicitly define which fields of our object we wish to copy. Any fields not included in the list will not be included in the copy and their default values will be used:

copy = det@(age)

//copy == 12, john, doe, 0

Note above that since we have defined a default value for the firstname and sirname fields, these default values are used for the copy. Otherwise, as in the case of friendCount, the default value for the type is used (0 for integer).

Exclusion list?

We can explicitly define an exclusion list, meaning "copy every field except for those defined in the list". Example:

copy = det@(<age>)//do not copy the age field instead populate with default value

//copy == 21, dave, whatson, 5

Overriding fields?

We can override the value of an object field as follows:

copy = det@(age = 300, firstname = "fred")

//copy == 300, fred, whatson, 5

Nested copy definitions?

We can control the way in which fields which are themselves objects are copied by using a nested copy definition referencing said field:

class Link(count int, details Details){
  def toString() => "{(count, details)}"
link = new Link(2, det)

lcopy = link@(count, details@(age=18))//override age of details field

//lcopy == 2, 18, dave, whatson, 5

Super copy definition?

We can control the way in which the superclass of the object is copied by nesting a super copy definition as follows:

open class Parent(a int=12, b int=13)
class Child(a int, b int, c int) < Parent(a, b){
  override toString() => "{(super.a, super.b, c)}"

cc = new Child(1, 2, 3)
ccopy = cc@(super@(a=89, b), c)

//ccopy == 89, 2, 3

Empty Copy?

We can choose to not specify any contents to the copy definition. In this instance we are instructing the copy operator to copy the object with all fields being of their default value:

copy = det@()

//copy == 21, john, doe, 0

Ignoring default values?

As we have seen previously, where a default value for a field is defined, this will be used if the field is excluded (either explicitly or implicitly) from the copy operation. To suppress this behaviour and instead use the default value for the type of the field, the nodefault keyword can be used:

copy = det@(;nodefault)

//copy == 0, null, null, 0

Unchecked copies?

By default Concurnas will check the copy definition to ensure that the fields referenced exist on object to be copied. In order to suppress this behaviour, the unchecked keyword can be applied:

asObject Object = det
copy = asObject@(age = 66; unchecked)

//copy == 66, dave, whatson, 5