TYPE CONSTRAINTS

As we saw in Chapter 2, one of the things we have to do when we define a type is specify the values that make up that type—and that’s effectively what a type constraint does. Now, in the case of system defined types, it’s the system that carries out this task, and there’s not much more to be said. In the case of user defined types, by contrast, there certainly is more to say, much more. So let’s suppose for the sake of the example that shipment quantities, instead of being of the system defined type INTEGER, are of some user defined type (QTY, say). Here then is a possible Tutorial D definition for that type:

  1.    TYPE QTY
  2.         POSSREP QPR
  3.               { Q INTEGER
  4.                   CONSTRAINT Q ≥ 0 AND Q ≤ 5000 } ;

Explanation:

  • Line 1 just says we’re defining a type called QTY.

  • Line 2 says quantities have a “possible representation” called QPR. Now, physical representations are always hidden from the user, as we know from Chapter 2. However, Tutorial D requires every TYPE statement to include at least one POSSREP specification,[113] indicating that values of the type in question can possibly be represented in some specific way; and unlike physical representations, possible representations—which we usually abbreviate to just possreps—definitely are visible to the user (in the example, users do definitely know that quantities have a possrep called QPR). Note carefully, however, that there’s no suggestion that the specified possible representation is the same as any physical representation, whatever that happens to be; it might be or it might not, but either way it makes no difference to the user.

  • Line 3 says the possrep QPR has a single component, called Q, which is of type INTEGER; in other words, values of type QTY can possibly be represented by integers (and users are aware of this fact).

  • Finally, line 4 says those integers must lie in the range 0 to 5000 inclusive. Thus, lines 2-4 together define valid quantities to be, precisely, values that can possibly be represented by integers in the specified range, and it’s that definition that constitutes the type constraint for type QTY. Observe, therefore, that such constraints are specified not in terms of the type as such but, rather, in terms of a possrep for the type. Indeed, one of the reasons the possrep concept is required in the first place is precisely to serve as a vehicle for formulating type constraints, as I think the example shows.

    Here’s a slightly more complicated example:

         TYPE POINT
              POSSREP CARTESIAN { X RATIONAL , Y RATIONAL
                      CONSTRAINT SQRT ( X ** 2 + Y ** 2 ) ≤ 100.0 } ;

Type POINT denotes geometric points in two-dimensional space; it has a possrep called CARTESIAN with two components called X and Y (corresponding, presumably, to cartesian coordinates); those components are both of type RATIONAL; and there’s a CONSTRAINT specification that (in effect) says the only points we’re interested in are those that lie on or inside a circle with center the origin and radius 100 (SQRT = nonnegative square root). Note: I used a type called POINT in an example in Chapter 2, as you might recall, but I deliberately didn’t show the POSSREP and CONSTRAINT specifications for that type at that time; tacitly, however, I was assuming the type had a possrep called POINT, not CARTESIAN. See the subsection immediately following.

Selectors and THE_ Operators

Before I continue with my discussion of type constraints as such, I need to digress for a few moments in order to clarify a few issues raised by the QTY and POINT examples.

Recall from Chapter 2 that types generally have certain associated selector and THE_ operators. Well, those operators are intimately related to the possrep notion; in fact, selector operators correspond one to one to possreps, and THE_ operators correspond one to one to possrep components. Here are some examples:

  1. QPR ( 250 )

    This expression is a selector invocation for type QTY. The selector has the same name, QPR, as the sole possrep for that type; it takes an argument that corresponds to, and is of the same type as, the sole component of that possrep; and it returns a quantity (that is, a value of type QTY). Note: In practice, possreps often have the same name as the associated type (I used different names in the QTY example to make it clear there’s a logical difference between the possrep and the type, but it would be much more usual not to). In fact, Tutorial D has a syntax rule that says we can omit the possrep name from the TYPE statement entirely if we want to, in which case it defaults to the associated type name. So let’s simplify the QTY type definition accordingly:

    TYPE QTY POSSREP { Q INTEGER CONSTRAINT Q ≥ 0 AND Q ≤ 5000 } ;

    Now the possrep and the corresponding selector are both called QTY, and the selector invocation shown above becomes just QTY(250)—which is the style I used for selectors in Chapter 2, if you care to go back and look. I’ll assume this revised definition for type QTY from this point forward, barring explicit statements to the contrary.

  2. QTY ( A + B )

    The argument to a QTY selector invocation can be specified as an expression of arbitrary complexity (just so long as it’s of type INTEGER, of course). If that expression is a literal, as it was in the previous example, then the selector invocation is a literal in turn; thus, a literal is a special case of a selector invocation (as in fact we already know from Chapter 2).

  3. THE_Q ( QZ )

    This expression is a THE_ operator invocation for type QTY. The operator is named THE_Q because Q is the name of the sole component of the sole possrep for type QTY; it takes an argument (specified as an arbitrarily complex expression) of type QTY; and it returns the integer that’s the Q component of the possrep for that specific argument.

As for type POINT, let’s first redefine that type so that the possrep has the same name as the type, as in the QTY example above:

     TYPE POINT POSSREP { X RATIONAL , Y RATIONAL CONSTRAINT ... } ;

Now continuing with the examples:

  1. POINT ( 5.7 , −3.9 )

    This expression is a POINT selector invocation (actually a POINT literal).

  2. THE_X ( P )

    This expression returns the RATIONAL value that’s the X coordinate of the cartesian possible representation of the point that’s the current value of variable P (which must be of type POINT).

Just as an aside, let me draw your attention to the fact that (as I said earlier) Tutorial D requires a TYPE statement to include at least one POSSREP specification. The fact is, Tutorial D does allow a type to have several distinct possreps. POINT is a good example—we might well want to define two distinct possreps for points, to reflect the fact that points in two-dimensional space can possibly be represented by either cartesian or polar coordinates. Temperatures provide another example—again, we might want to define two possreps, to reflect the fact that temperatures can be possibly represented in either degrees Celsius or degrees Fahrenheit. Further details don’t belong in a book of this nature; I’ll just note for the record that SQL has no analogous feature.

More on Type Constraints

Now let’s get back to type constraints as such. Suppose I had defined type QTY as follows, with no explicit CONSTRAINT specification:

     TYPE QTY POSSREP { Q INTEGER } ;

This definition is defined to be shorthand for the following:

     TYPE QTY POSSREP { Q INTEGER CONSTRAINT TRUE } ;

Given this definition, anything that could possibly be represented by an integer would be a legitimate QTY value, and so type QTY would necessarily still have an associated type constraint, albeit a rather weak one. In other words, the specified possrep defines an a priori constraint for the type, and the CONSTRAINT specification effectively imposes an additional constraint, over and above that a priori one. (Informally, however, we often take the term “type constraint” to refer to what’s stated in the CONSTRAINT specification as such.)

Now, one important issue I’ve ducked so far is the question of when type constraints are checked. In fact, they’re checked whenever some selector is invoked. Assume again that values of type QTY are such that they must be possibly representable as integers in the range 0 to 5000 inclusive. Then the expression QTY(250) is an invocation of the QTY selector, and that invocation succeeds. By contrast, the expression QTY(6000) is also such an invocation, but it fails. In fact, it should be obvious that we can never tolerate an expression that’s supposed to denote a value of some type T but in fact doesn’t; after all, “a value of type T that’s not a value of type T” is a contradiction in terms. Since, ultimately, the only way any expression can yield a value of type T is by means of some invocation of some selector for type T, it follows that no variable—in particular, no relvar—can ever be assigned a value that’s not of the right type.

One last point to close this section: Declaring anything to be of some particular type imposes a constraint on that thing, by definition.[114] In particular, declaring attribute QTY of relvar SP (for example) to be of type QTY imposes the constraint that no tuple in relvar SP will ever contain a value in the QTY position that fails to satisfy the QTY type constraint. (As an aside, I note that this constraint on attribute QTY is an example of what’s sometimes called an attribute constraint.)



[113] There are some minor exceptions to this rule that need not concern us here.

[114] I would much have preferred to use the more formal term object in this sentence in place of the very vague term thing, but object has become a loaded word in computing contexts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.108.111