61 Attributes
All attributes hold a set of values
Under the formalism all attributes are unary predicates that record their extensions (where an extension is a set of values).
Therefore, in the formalism all attributes hold a set of values. This is regardless of whether an attribute is constrained to hold exactly one value. That is a constraint on the set; it does not mean a set is not present under the formalism.
This includes a boolean-valued attribute. A Boolean attribute is still a unary
predicate whose values have type Boolean; when constrained to exactly one value,
its extension contains exactly one Boolean value.
Multiplicity on attributes
Consider the following definition of type Supplier:
entity Supplier
{
Integer id;
@predicate("The supplier is located in [city]")
City city*;
}
The asterisk on attribute city means a supplier may have zero or more cities.
Accordingly, the extension of its predicate may contain zero or more values of type City.
The asterisk qualifies the attribute, not its type.
While a supplier may be located in several cities, each city is identified by
one value of type City.
The syntax should not be interpreted as: cities : City*. That would
correspond to the predicate:
The supplier is located in [cities].
which would always have exactly one tuple making it true, regardless of how many cities the supplier is located in - including none at all.
This interpretation is antithetical to how Codd envisaged the RM to be used. It breaks 1NF which requires attributes to be atomic, in other words not embedding collections inside attributes.
Proper use of Predica for constructing an information system grounded in logic is to
emphasise singular attribute names and avoid attribute types that give rise
to set-valued attributes.
This gives a more helpful meaning to the term atomic. It has nothing to do
with internal complexity of type City and everything to do with attribute city
being singular.
Predica has a dedicated mechanism for dealing with multiplicity: a cardinality constraint on the extension of the attribute.
It is important to understand that this mechanism handles multiplicity through constraints on attribute extensions, not through plural attribute names or by introducing an army of additional attribute types for “types of sets”.
Attributes represent functions
Consider a value or entity type S containing attributes a, b, c, d, e and f as follows:
value S
{
T a;
T b?;
T c*;
T d+;
T e[2];
T f[4..10];
}
Every attribute of S denotes a function
\[
x : S \to \mathcal{P}(T),
\]
where the multiplicity annotation constrains the cardinality of each set \(x(s)\) for \(s \in S\).
The different annotations correspond to the following constraints:
-
a : Tdenotes a function \(a : S \to \mathcal{P}(T)\) such that \(|a(s)| = 1\) for all \(s \in S\). This can be identified with a total function \(a : S \to T\). -
b? : Tdenotes a function \(b : S \to \mathcal{P}(T)\) such that \(|b(s)| \in \{0,1\}\) for all \(s \in S\). This can be identified with a partial function \(b : S \rightharpoonup T\). -
c* : Tdenotes a function \(c : S \to \mathcal{P}(T)\) with no lower bound on \(|c(s)|\); the set may be empty. -
d+ : Tdenotes a function \(d : S \to \mathcal{P}(T)\) such that \(|d(s)| \ge 1\) for all \(s \in S\); that is, each instance of \(S\) is associated with at least one value of type \(T\). -
e[2] : Tdenotes a function \(e : S \to \mathcal{P}(T)\) such that \(|e(s)| = 2\) for all \(s \in S\). -
f[4..10] : Tdenotes a function \(f : S \to \mathcal{P}(T)\) such that \(4 \le |f(s)| \le 10\) for all \(s \in S\).
In all cases, the attribute name remains singular because it denotes a single function (or unary predicate) whose values are constrained only by the specified multiplicity.
Const attributes
A base attribute may be declared const. Its extension is established when an
instance is constructed and remains unchanged for the lifetime of that instance.
Different instances may have different extensions for the attribute.
entity Document
{
const Integer id;
String title;
}
var document1 = Document { id = 1, title = "First" };
var document2 = Document { id = 2, title = "Second" };
A const attribute cannot be assigned a value in its declaration:
entity Document
{
// Error: cannot give const attribute a value in its declaration
const Integer id = 1;
}
Its extension depends on the instance being constructed, so it must instead be supplied when that instance is initialised. Every constructor must initialise each required const attribute exactly once. After construction, an update whose target is that attribute is invalid.
Const attributes are therefore dynamic rather than static. Their values are determined at run time as instances are created, and may vary from one instance to another. What is constant is not a value at the level of the type, but the result of the attribute function for a particular instance after its construction. A constant definition denotes one fixed value independently of any receiver; a const attribute denotes instance-dependent information that becomes immutable once the receiver exists.
Derived attributes likewise cannot be the targets of update statements, but they are
not declared const. The extension of a derived attribute is determined by its defining
expression and may therefore vary over time as its inputs change, even though it cannot itself
be updated directly. This is a semantic distinction, not a storage
requirement: a compiler may materialise a derived attribute as a cache, provided the cached extension
is kept consistent with its defining expression. A const base attribute, by contrast, has an
extension established during construction and preserved thereafter. Thus not an update
target does not by itself imply immutable.
Const attributes in type expressions
Within the type in which it is declared, a const attribute is available as though it were a static constant. It may therefore be used as a static value argument in the type of a subsequent attribute. The value is scoped to the instance: it is fixed during construction, but different instances may supply different values.
value Image<Type T>
{
const Uint32 width;
const Uint32 height;
Array<T, width, height> pixels;
}
For a particular Image<T>, width and height determine the two static extents
of pixels. For example, when an image is constructed with width 640 and height
480, its pixels attribute has type Array<T,640,480>. The constructor must
supply an array with those extents.
Because width and height are const, the dimensions cannot subsequently change and
invalidate the type of pixels. They behave as static constants while type-checking
the members of that image, even though their values are selected dynamically when the
image is constructed. This is instance-scoped static information, not one pair of
dimensions shared by every Image<T>.