Shared Reference

Shared references are one of those language features that must be handled with care. In TypeScript, for example, the following code will pass type checking.

class NormalMember {}
class VIPMember extends NormalMember {}

let vip_area: VIPMember[] = []
let normal_area: NormalMember[] = vip_area

normal_area.push(new NormalMember())
console.log(vip_area) # [NormalMember]

A NormalMember has entered the vip_area. It is an obvious bug, however what went wrong? The cause is the shared reference denatured. The normal_area is created by copying the vip_area, but in doing so the type has changed. But VIPMember inherits from NormalMember, so VIPMember[] <: NormalMember[], and this is not a problem. The relation VIPMember[] <: NormalMember[] is fine for immutable objects. However, if you perform a destructive operation like the one above, there will be a breakdown.

In Erg, such code is played back due to the ownership system.

NormalMember = Class()
VIPMember = Class()

vip_area = [].into [VIPMember; !_]
normal_area: [NormalMember; !_] = vip_area

normal_area.push!(NormalMember.new())
log vip_area # OwnershipError: `vip_room` was moved to `normal_room`

However, it can be inconvenient for an object to be owned by only one place. For this reason, Erg has a type SharedCell!T!, which represents a shared state.

$p1 = SharedCell!.new(!1)
$p2 = $p1.mirror!()
$p3 = SharedCell!.new(!1)
# If $p1 == $p2, a comparison of the content type Int!
assert $p1 == $p2
assert $p1 == $p3
# Check if $p1 and $p2 point to the same thing with `.addr!`.
assert $p1.addr!() == $p2.addr!()
assert $p1.addr!() != $p3.addr!()
$p1.add! 1
assert $p1 == 2
assert $p2 == 2
assert $p3 == 1

Objects of type SharedCell! must be prefixed with $. Also, by their nature, they cannot be constants.

The SharedCell! T! type is also a subtype of T! and can call methods of type T!. The only methods specific to the SharedCell!T! type are .addr!, .mirror! and .try_take.

An important fact is that SharedCell! T! is non-variant, i.e., no inclusions are defined for different type arguments.

$vip_area = SharedCell!.new([].into [VIPMember; !_])
$normal_area: SharedCell!([NormalMember; !_]) = $vip_area.mirror!() # TypeError: expected SharedCell!([NormalMember; !_]), but got SharedCell!([VIPMember; !_])
# hint: SharedCell!(T) is non-variant, which means it cannot have a supertype or a subtype.

However, the following code have not problem. In the last line, it's the VIPMember argument that has been typed converted.

$normal_area = SharedCell!.new([].into [NormalMember; !_])
$normal_area.push!(NormalMember.new()) # OK
$normal_area.push!(VIPMember.new()) # OK