An object of type Group, may have some members like values, and some other as pointers to values. Some objects are used as values also. A group object can be a value or a pointer to object.
Here we will see a subset of properties of groups about copy, deep and shallow.
About Check1 module:
We have a class named alfa which make a group of 4 members, a, b, k() and beta with member z. From these members objects are array k() and group beta. These are "values" for interpreter. See the add() which take a pointer to array one time and a double at second time and the semantic a+=m works for either type. The & symbol used for passing by reference.
About Check2 module:
Now our alfa class has k as pointer to array (we make it assign an empty array), and beta as pointer to group (we make it assign the null object, which is not Null but an object of type Null). We have a Class: part. This part say to interpreter that the following members exist only on construction time. Module alfa called when we use alfa() to make a new group of type alfa. Also we have a private class betaClass. A class definition which isn't member is global, but as member of group is local. So this class used to make the object which place to member beta. We do the work to place numbers to array which point the member k, and the object to beta from the constructor of alfa.
To use the z member of beta inner object we have to use the fat arrow. So for z, the z.beta=>z used for return or set value. See that between z and beta there is no fat arrow (this means that z isn't pointer to group, so beta is a normal member - a normal member can be pass by reference), So how we pass by reference the z member of beta? To do that we have to "open" the beta using for beta { }. At this point the block used for temporary definitions (all new definitions deleted after the exit from this block). So one temporary hidden variable is the variable which hold the group, not as pointer but as normal group. So tge add(&.z, 100) say that .z is member of the first hidden variable (we can use ten hidden variables, adding a list of variables one one or more nested for object blocks.
We see here that we have a shallow copy. The x group created from z but have the same values for k and beta (x get a copy of pointers, not a copy of those which points). So changing values from z side we get the same change from x side. When we do x=z we merge z to x, so we get copy from pointers not the copies of actual objects which these pointers points.
About Check3 module:
We change the class alfa to get a Value part. This part make the group to return what we want to return. When we do this, without made a Set part, then our object block any merging from other object. So the X=Z first time can be done because X has no value/type, but second time X=Z can't be done, there is no Set part. The value part, first time make the m from This (we use This like This of java, but we didn't use it - we can - for members inside a function member, we just use dot and identifier which is the same as This dot identifier). Next at the value part we make a new pointer making a new object of class betaclass passing the same value of this.beta=>z. Also we make a copy of this.k using the cons(array, ....) which concatenate arrays by using shallow copies of them. Last we return the group(m) because this function (group()) can get a copy of the object without using the value part (using =m and not =group(m) we call again the value part of that m, until we fill all the process stack). So the Value part make a new object with deep copies of the two objects k and beta. So when we do the shallow copy at x=z then the m which is a deep copy then by shallow copy we get a real deep copy (the m destroyed silentlly) . At the end of then Check3 module we check for the second x=z inside a Try block. So we trap the error and continue,
About Check4 module:
Now we make the Set part. This part has only one statement Read This, so we get to This the merged group. If we want to get a group of a specific type we can use Read m as thatType: Let This=m (we can't use This=m inside set part because is like we do recursion, and we fill the process stack). As you see there is one condition for each Value and Set functions, which excaust the stack, which raise the "out of memory" error. The let this=m is actually a Push m and a Read This.
Set {
read m as alfa: let this=m
}
Better is this (Read This as alfa). But see the example. Using Let we can merge another group. See also the final clause for value and set. This means that these functions are final, they can't change from other merging. Merging is a "live" inheritance. Not only merge members, but also the final group has all the types from the merged objects.
class alfa {
double x
set final {
read this as alfa
}
value final {
=this
}
class:
module alfa (.x) {}
}
z=alfa(100)
? z.x=100
m=alfa(300)
class beta {
x=500
}
try {
z=beta()
}
? z.x=100
z=m
? z.x=300
' let z=beta() skip the set part
let z=beta()
? z.x=500
? z is type alfa = true
' now z is also type beta
? z is type beta = true
k=beta()
k.x=1000
try {
z=k
} ' can't because now we not skip the Set part
So for this module, the second x=z run without raising error.
About Check5 module:
This is the advanced test. The z now is pointer to object. We make the Remove { } part which run when the object deleted. The remove part is the deconstructor. So now we have constructor module alfa, value, set and deconstructor called remove.We place a print statement at value part to identify when we use the value part and when we skip it. Because z is a pointer, to use the value part we have to do explicitly, using eval(z) . Inside Value part the This not call the Value part, and not return the pointer (the z value) to m. So value part not changed from Check4 and Check3 modules.
So if we use x=z the x will be a pointer to the same object which z points. We use x=Eval(z) so we get a group copy of actual object which z point.
So how we send by reference z.k and z.beta.z when z is a pointer and beta is a pointer too?
for z {
add(&.k, 20)
for .beta {
add(&..z, 100)
}
}
As we see from code we have two for object, the second nested, and we send .k and ..z (see the double dot before the name of z).
At the second part of this module we set z at another object, the x. First the old object deleted by calling the constructor before actual deleted.
Now the z pointer isn't a real pointer but a "reference type". Because the x is a "named" group, which means a group with a name (like z and x in module Check1, which they are attach to module like variables), when we do z->x or z=pointer(x), interpreter save a weak reference and the status "i am a reference", and when we use the pointer the interpreter make the hard link (but maybe that can't be done, if the refernced object not exist, because module where this object attached now not run, so an error raised). A pointer to object may change to normal pointer or to weak reference depends from the assigment.
We see next the z->(x) which make a copy of x (deep copy, we will see the output of print statement from value part of class alfa). The z->(x) is same as z=pointer((x)) (see the double parenthesis, when we didn't place parenthesis then we have either a group or a class name with parentesis and maybe values for constractor), but when we have "extra" parenthesis then interpreter expect a goup object from expression inside parenthesis.
The last thing we see in Check1 is the test for null for the pointer z. We place the null (pointer() without something). There is no NULL as the 0 address from other programming languages. Although we can do that: z->0 which is the same as z=pointer() (we can't use z->pointer() because pointer() isn't a user class but an internal function). The Null object can't merged to group (there is no members and the type Null can't be passed to other objects). When interpreter start make the Null object, with a specific address. So the null pointer internal isn't null. For us the Null object is the Null object. So the z is type Null return True if z point to Null object.
For other objects like arrays (of type mArray) there is no Null object. We can place an empty array but the test of two array pointers which have empty arrays point to different empty arrays:
a=(,)
b=(,)
? a is b = False
NullArray=(,)
a=NullArray
b=NullArray
? a is b = True
Although using this is like a Null array, there are some functions which append members. and the NullArray isn't blocked for appending members. So from the time we accidentally place members we break the use of NullArray.
module Check1 {
print module.name$
class alfa {
double a, b
dim k(1 to 10)=10
group beta {
z=100
}
}
z=alfa()
? z.k()#sum()=100 ' True
x=z ' deep copy because of k() and Group
add(&z.k(), 20)
add(&z.beta.z, 100)
? z.beta.z=200
? z.k()#sum()=300
? x.K()#sum()=100
? x.beta.z=100
x=z ' we do a merge and the values changed
? x.K()#sum()=300
? x.beta.z=200
sub add(&a, m)
a+=m
end sub
}
Check1
module Check2 {
print module.name$
class alfa {
double a, b
k=(,)
beta=pointer()
private:
class betaClass {
z
class:
module betaClass (.z) {
}
}
class:
module alfa {
dim a(1 to 10)=10
.k<=a()
.beta<=pointer(.betaClass(100))
}
}
z=alfa()
? z.k#sum()=100 ' True
x=z ' shallow copy because of k and beta are pointers to objects
add(&z.k, 20)
for z.beta {
add(&.z, 100)
}
? z.beta=>z=200
? z.k#sum()=300
? x.K#sum()=300
? x.beta=>z=200
? x.beta is z.beta = true
? x.k is z.k = true
? x is z = false
x=z ' we can do that, but objects but we have shallow copy too
? x is z = false ' x internal pointer can't change
' x and z are objects with unique and private pointer
' x and z deleted when this module exit run
sub add(&a, m)
a+=m
end sub
}
Check2
module Check3 {
print module.name$
class alfa {
double a, b
k=(,)
beta=pointer()
value {
m=this
m.beta=pointer(.betaClass(.beta=>z))
m.k=cons(.k)
=group(m)
}
private:
class betaClass {
z
class:
module betaClass (.z) {
}
}
class:
module alfa {
dim a(1 to 10)=10
.k<=a()
.beta<=pointer(.betaClass(100))
}
}
z=alfa()
? z.k#sum()=100 ' True
x=z ' deep copy because now object handle the export (has Value part)
add(&z.k, 20)
for z.beta {
add(&.z, 100)
}
? z.beta=>z=200
? z.k#sum()=300
? x.K#sum()=100
? x.beta=>z=100
// we can't change it because we didn't handle the import (has no Set part)
Try ok {
x=z
}
If not ok then Print "Error:";Error$
sub add(&a, m)
a+=m
end sub
}
Check3
module Check4 {
print module.name$
class alfa {
double a, b
k=(,)
beta=pointer()
value {
m=this
m.beta=pointer(.betaClass(.beta=>z))
m.k=cons(.k)
=group(m)
}
Set {
read this
}
private:
class betaClass {
z
class:
module betaClass (.z) {
}
}
class:
module alfa {
dim a(1 to 10)=10
.k<=a()
.beta<=pointer(.betaClass(100))
}
}
z=alfa()
? z.k#sum()=100 ' True
x=z ' deep copy because now object handle the export (has Value part)
add(&z.k, 20)
for z.beta {
add(&.z, 100)
}
? z.beta=>z=200
? z.k#sum()=300
? x.K#sum()=100
? x.beta=>z=100
' now we can merge z to x because we have a Set part
x=z
? x.K#sum()=300
? x.beta=>z=200
sub add(&a, m)
a+=m
end sub
}
Check4
module Check5 {
print module.name$
class alfa {
double a, b
k=(,)
beta=pointer()
value {
? "value ok"
m=this
m.beta=pointer(.betaClass(.beta=>z))
m.k=cons(.k)
=group(m)
}
Set {
read this
}
remove {
print "just deleted", .a, .b
}
private:
class betaClass {
z
class:
module betaClass (.z) {
}
}
class:
module alfa(.a, .b) {
dim a(1 to 10)=10
.k<=a()
.beta<=pointer(.betaClass(100))
}
}
z->alfa(10, 30)
? z=>k#sum()=100 ' True
' eval(z) call value when z is a pointer to group
x=eval(z) ' deep copy because now object handle the export (has Value part)
' x=group(z) 'shallow copy for members which are pointers to objects
for z {
add(&.k, 20)
for .beta {
add(&..z, 100)
}
}
? z=>beta=>z=200
? z=>k#sum()=300
? x.K#sum()=100
? x.beta=>z=100
? "now z will point to x, so old object deleted"
' z=pointer(x)
z->x
? "now z point to x"
? z=>beta=>z=100
? z=>k#sum()=100
? "now z point to a copy of x (as a deep copy)"
x.a+=100
z->(x)
for z {
add(&.k, 20)
for .beta {
add(&..z, 100)
}
}
? z=>a=110
? z=>beta=>z=200
? z=>k#sum()=300
? x.K#sum()=100
? x.beta=>z=100
? z is type alfa=true
? "now z point to null, old deleted"
z=pointer()
? z is type null=true
sub add(&a, m)
a+=m
end sub
}
Check5