Share via


Constraints on Types

[This content is no longer valid. For the latest information on "M", "Quadrant", SQL Server Modeling Services, and the Repository, see the Model Citizen blog.]

A constraint is a logical predicate that must be satisfied by all values in a type.

This topic describes how to apply a predicate to a type by using the where key word. The effect of applying the constraint is that type values must be of the ascribed type and also satisfy the predicate specified in the where clause.

You apply the additional constraints to a given type with the where keyword, typically to use a value of the derived type in a context that has differing requirements.

Another use of constraints is to put requirements on entity collections. For more information, see unique ("M" Keywords) and identity ("M" Keywords).

Finally, you can constrain two entity collections. For more information, see One-to-Many Relationships.

Constraint Examples

Here is an example of a constraint applied to an intrinsic type.

type CodedName : Text where value.Count < 7;

In this example, you have constrained the possible Text values to those in which the value contains less than seven characters. That means that the following declarations hold true.

"Terse" in CodedName
!("Verbose" in CodedName)

Constraint Validation

Microsoft code name “M” ensures that constraints are met. When possible, this is done at compile-time, and is known as static enforcement. But sometimes this is not possible, in which case, checking is done at runtime and is known as dynamic checking.

“M” programs try to report any constraint violations before the first expression in a “M” program is evaluated at runtime. This is called static enforcement and it can generate errors similar to a syntax error. However, some constraints can only be enforced against live data and therefore require dynamic enforcement.

In general, the “M” philosophy is to make it easy for the user to write down their intention and put the burden on the “M” program to make it work. However, to allow a particular “M” program to be used in diverse environments, a fully-featured “M” program should be configurable to reject “M” programs that rely on dynamic enforcement for correctness to reduce the performance and operational costs of dynamic constraint violations.

Static Constraints

Static constraint checking can be done when all of the information required to validate the expressions is available at compile-time. In the following examples, “M” is able to do this because the expressions express their operands in terms of built-in operators over constants.

Consider the following type definition.

type SuperPositive : Number where value > 5;

Assume that there is a function named CalcIt that is declared to accept a value of type SuperPositive as an operand. The “M” compiler validates any expressions that call the function.

The following expressions validate successfully.

CalcIt(20)
CalcIt(42 + 99)

The following expressions generate an error at compile time.

CalcIt(-1)
CalcIt(4)

Dynamic Constraints

If an expression uses dynamic sources of data or user-defined functions, then it is necessary to use the type ascription operator to assert that a value conforms to a given type.

For an example of this, use the preceding example and assume that there is a second function, GetVowelCount, which requires an operand of type Text and returns a value of type Number that indicates the number of vowels in the operand.

Because it is not known based on the declaration of GetVowelCount whether its results are greater than five or not, the following expression is not a legal “M” expression.

CalcIt(GetVowelCount(someTextVariable))

Because the declared result type (Number) of GetVowelCount includes values that do not conform to the declared operand type of CalcIt (SuperPositive), “M” treats this expression as an error and does not evaluate the expression.

You can rewrite this expression to avoid this error by using the type ascription operator.

CalcIt( (GetVowelCount(someTextVariable) : SuperPositive) )

This tells “M” that the GetVowelCount function always returns a value that conforms to the type SuperPositive.

Because the CalcIt function is declared to only accept values that conform to SuperPositive, the system ensures that all values passed to it are greater than five. To ensure this constraint is never violated, the system may be required to inject a dynamic constraint test that may fail when evaluated. This failure occurs when the expression is actually evaluated, rather than at compile-time as happens with static constraint evaluation.