Records (Mutable)
The Slang record syntax is similar to that of Slang data types, with
additional features added to support mutability. Records can have a
mix of mutable and immutable fields. var
is used to indicate
mutability for both fields listed
in the class declaration/constructor as well as for internal fields.
As with Slang data types, fields without a val
/var
annotation are
implicitly val
. For internally declared fields, private
can
also be used to control visibility (this also applies to data types).
@record class MutCoordinate(var x: Z, var y: Z, z: Z) {
// var fields (e.g., x and y) are mutable.
// Fields without var/val (e.g., z) default to val.
// val fields are immutable.
var used: B = F // Records can also have var/val fields whose initial values
// are not provided via the constructor.
private var flagged: B = F // internally declared fields can be declared private, as in Scala
def isCenter: B = { return x == 0 && y == 0 && z == 0}
def isEqualX(other: Coordinate): B = {
return x == other.x
}
def isEqualY(other: Coordinate): B = {
return y == other.y
}
}
The example client code below illustrates how var
record fields
can be updated, whereas val
fields cannot. Note that there is a
distinction between the mutability of identifier/variables and the
mutability of structures to which they bind. For example, val
identifiers can be bound to records – in such cases, the record
structure can be updated, but the identifier binding itself cannot.
Similarly, var
identifiers can be bound to immutable data types –
a new data type instance may be assigned to the variable, but the
structure itself cannot be changed.
val c4: MutCoordinate = MutCoordinate(5,7,20) // construct a record instance
// A val (immutable var) can used to refer to a mutable structure.
// While fields of the structure can be updated, the variable itself cannot.
c4.x = 6
// c4.z = 21 // ERROR: assignment to val fields of record is not allowed.
c4.used = T // Public internal fields can be assigned.
// c4.flagged = T // ERROR: private internal fields cannot be assigned
The examples below illustrate Slang’s copy-on-demand semantics for records (an explanation follows below).
val c5 = c4 // When Slang mutable structures are assigned to other variables,
// they are implicitly copied to avoid aliasing. In this case,
// a copy of the record bound to c4 is created to bind to c5.
c4.y = 8 // Update a field of c4
assert(c4.y != c5.y) // Record bound to c4 is not shared with c5
// (update to c4 is not reflected in c5).
var c6 = c5(y = 8) // The "copy syntax" used for data types can also be used for records.
assert(c4 == c6) // Equality is structural.
When the value of c4
is assigned to c5
, c4
’s value is
notionally copied to c5
. The Slang compiler does not
necessarily cause the copy to be implemented immediately. The effect
of the copy can be seen in that the assignment to c4.y
does not
affect the value of c5.y
, as indicated by the following
assertion.
The struct(field1 = value1, fieldn = valuen) notation for creating a copy of the structure with the same value except for changes to the listed fields can be used for records as well as data types.
Note
JH: To Robby, the Slang By Example includes an additional example
where the isEqual method is overridden. I wasn’t sure how to explain
or motivate this. I was worried that overriding the definition of
==
would generate problems with the logic (i.e., changing the
notion of equality). Are there some properties that the new equality
must maintain?