module Record: sig end
A record is a collection of elements munished with a void topology (an element has no neighbors). The elements of a record are the values associated to the fields of the records.
A fields is any comparable MGS value, achieving the idea of a dictionary.
However, the usual use of record is to use symbols
as fields and this usage
is supported by a special syntax and by type declarations. When all the fields
of a records are symbol, they can be manipulated exactly like ordinary records
in Pascal (or struct in C) with the fields denoted by simple identifiers.
The element of a record are acessed using the 'take
function:
take(record, field)
returns the value associated with field
in the record. This usage complies with
the idea that the fields are the positions of a record.
record.(field)
is an infix syntax for take(record, field)
.
If field
is a symbol, says `id
, then
record.id
abbreviates record.(`id)
which is itself an abbreviation of take(record, `id)
One can defines record's type if all implied fields are symbols.
A record declaration is introduced by the keyword record
followed by the name of the type
and by a record specification expression after an equal sign. Here is an example:
record S = R + { a, b = ..., ~c }
A record specification expression is a sum of record type names and record specification lists.
In the previous example, R
is the name of a record type which must have been previously declared,
and { a, b = ..., ~c}
is a record specification list. The idea is that the fields of R
must be the fields specified in S
augmented by the fields specified in the record specification list.
A record specification list is a list of record items specification separated by comma. A record item specification is either
{a, b}
which defies a record with two fields a
and b
. recall
that when we speak about record types, all field are symbols. Then, when we say that a record has
fields a
and b
we mean that the record is a dictionary with two entry, the symbols `a
and `b
.
{~c}
: this means that the record must not have a field nammed c
.
{b:int}
which means that the record must have a field b
and
that the value of b
must be an integer. Any predicate can follow the :
{a = v}
which specifies a record that must have the
field a
with an associated value of v
.
{a <> v}
which specifies a record that has a field a
but does not have the value v
The record types are in a subtyping relationships. For instance, the type {}
is the most general
record type. That is
record Rtop = {}
introduces the type Rtop
which subsumes all others record type. In other word, if r
is a record,
then Rtop(r)
always returns true
.
A simple field name a
subsumes a type qualified name a:...
and a value qualified name
a=...
.
Type qualified names are incomparable, as well as value qualified names. For example, if we have
record R = {a};; record S = {a=0};;
then for any record r
such that S(r)
holds, we have also R(r)
. Note that if a record type Q
is defined as
record Q = ... + T + ...
then Q
is a subtype of T
.
A record item can precise an item specification inherited through a recod type name. For example, in:
record R = {a};; record S = R + {a=3};;
the record item a=3
makes in the specification of S
, the definition of the fields a
inherited from R
more precise. If the specification are not compatible, an error is raised.
In the example below, both S
and R
use incompatible field specification for a
:
[record R = {a:int};; record S = R + {~a};; record T = R + {a:\x.false};;
(recall that type qualified name are incomparable).
A record type defioens a predicate with the same name. The predicate returns true if its argument
is a record that fulfills all the record items specification of the type. NOTE that it can have
additional fields as well.
'dot : record -> string -> any
dot(r, "xxx")
returns r.xxx
. Note that the second
argument of the dot operator is a string (a value) while in the
notation r.xxx
the second argument is a syntactic identifier (not
a value). This operator makes able to compute the field that you
want access:
dot({a1 = 1, a2 = 2, a3 = 3}, "a"+(3-2))
returns the value of the field a1
because "a"+(3-2)
evaluates
to "a1"
. 'set_field : record -> string -> any -> record
'set_field(r, "a", 0)
creates or changes the value of the field
a
in record r
. This change is applicative: a new record is
created with the field a
updated and returned. An equivalent form
is r + {a=0}
but 'set_field(r, "a", 0)
enables the denotation of
the fields records as strings. This function enables teh creation of
fields with arbitrary name (including name not accepted in the dot
notation). 'remove_field : string -> record -> record
'remove_field("a", r)
creates a new record similar to the record
r
except that there is no field a
. If field a
does not
exists in r
, the result is a copy of r
.'remove_field("a",{x=1, a=2})
returns the (new) record {x=1}