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