The heart of any Rosetta specification is a collection of items that represent observable quantities associated with a system. They represent a collection of things that, if properly described, provide a precise system model that can be reasoned about to predict behaviors. By defining item properties and relationships between items, a Rosetta specification defines expectations on their collective behavior and the system they represent.
An item is defined by a declaration that associates it with a type and optional value. The type represents the collection of values the item can legally assume. Rosetta types are formed from sets using traditional set operations, comprehension and extension. The optional value makes an item a constant by associating a specific value with it. Values are defined in the traditional sense as terms that are in irreducible, normal form.
Like type systems in programming languages, the Rosetta type system specifies constraints by associating a type, like integer
or set
(character)
, with an item. Unlike traditional programming language type systems, Rosetta types may be defined by comprehension using a property to filter an existing type. Thus, a Rosetta type asserts properties on an item by restricting it to a set of values or defining a property the item must have to be of a particular type. Using types to specify properties in this way is a critical part of Rosetta specification. The first step in this process is understanding item declaration and how types and values are associated with an item.
All Rosetta definition structures are represented semantically as items with a label, type, and value properties. The item label provides a name for the item that is used to reference it in the specification. In the Rosetta expression x=y+1
, the labels x
and y
refer to the declared items with those respective labels. The item value specifies a value associated with the item. In the Rosetta expression x=y+pi
, the value of the item labeled x
is constrained to be equal to the value of the item labeled y
plus the value of the item labeled pi
. The item type is a set that specifies a collection of values the item can legally take. Given an item v
whose type is the set T
, the value associated with v
must always be an element of the set T
.
A Rosetta label represents a legal name for a Rosetta item. The rules for writing labels in Rosetta are similar to those of other languages. A label must start with a letter, which may be followed by any number of letters and digits. Single underscore characters (_
) may be included to enhance readability, but they may not occur at the beginning or end of a label. Examples of legal labels are:
a N timeout MaxTravel Min_Travel port1 port_2a
Examples of illegal labels are:
3a // starts with a digit _id // starts with underscore packet_ // ends with underscore sequence__0 // two successive underscores
The case of characters in labels is not significant. The following labels are all treated as the same:
pressure Pressure PRESSURE
While we have a great deal of flexibility in choosing item labels to aid readability of specifications, there are some labels we cannot use, since they are reserved as keywords. These keywords provide the syntactic structure of a Rosetta model and are listed in Table 2.1. Note that, as in item labels, case is insignificant. In this book, we follow the convention of setting keywords in boldface to distinguish them from item labels.
Table 2.1. Rosetta keywords
and | data | facet | let | or | then |
array | definitions | false | library | output | top |
assumptions | design | forall | of | translators | |
as | div | from | max | true | |
dom | functors | min | package | type | |
be | domain | mod | |||
begin | if | multiset | ran | use | |
between | element | implications | rem | ||
body | else | implies | nand | ret | var |
bottom | elseif | in | nmax | ||
end | infinity | nmin | sel | where | |
case | enumeration | input | not | sequence | with |
combinators | exists | interaction | nor | set | |
component | export | interface | null | sharing | xnor |
constant | is | sub | xor | ||
subtype |
A Rosetta value is a structure or term in normal form that cannot be further evaluated. Although not all normal forms are values, all values are normal forms. The simplest Rosetta values represent numbers, characters, and booleans. Examples of such values include the numbers 1
and 3.1415
, the characters ‘a’
and ‘@,’
and the boolean constants true
and false
. Such values are elemental values and cannot be decomposed. We form composite values from other values like the constant sequence [1,2,3,4]
, the constant set {high, low, unknown}
, and the multiset constant {*1,2,3,1,2*}
. The sequence of sets [{}, {1}, {2,3}]
is also a composite value because it is formed from other values.
All elements of a specification are items that have associated types and thus potential values — a function item has an associated function value, a facet item has an associated facet value, and an element of a constructed type has a constructed value. A function value is an anonymous function that encapsulates an expression with a collection of parameters. Rosetta function values are pure functions in that they may only specify a functional relationship between their parameters and output values. A facet value represents the basic unit of Rosetta specification and is associated with facets, domains, components, and packages. Finally, constructed values result from the application of constructors associated with constructed types. Rosetta constructed types provide mechanisms for users to define completely new types by specifying type constructor functions and observer functions for a collection of values.
A Rosetta type defines a collection of values that constrains the value of an item. Within the Rosetta language system, all types are values and appear as sets. Technically, not all types are sets, but set operations are always available on them. This fact and its implications will be discussed extensively in subsequent chapters. For now it is sufficient to understand that an item’s value must be an element of its type.
If one Rosetta type is a subtype of another, then it follows that every element of the subtype is a member of the supertype. This is precisely the definition of subset. It follows that Rosetta subset relations also indicate subtype relationships. The following relationships are examples of this principle:
integer =< real // integer is a subtype of real integer < real // integer is a proper subtype of real real >= integer // real is a supertype of integer real > integer // real is a proper supertype of integer
Rosetta item values must always be taken from their associated types. This fact is the heart of Rosetta type and constraint checking. As we will see, using a sophisticated and flexible type system allows assertion and verification of complex properties in a compact and readable form.
Item declarations occur in declarative regions, parameter lists, functions, and let
forms where new variables and constants are required. Each item declaration creates a new item with a label and must be annotated with a type assertion. A Rosetta type assertion uses the assertion operator “::
” to associate a type with an item. Whenever we use the notation x::T
, we are asserting that the value associated with the item labeled x
is a member of the type T
. All declarations must include a type assertion, but type assertions may be used to indicate types anywhere in a Rosetta specification. Thus, type assertions are used to assign types to new items and to specify types for items in expressions.
When the type assertion x::T
appears in a declarative region such as a facet’s local declarations, a let
clause or parameter list, it represents an item declaration or simply a declaration. A declaration of this form adds a new item labeled x
to the current lexical context with the assertion that x
is a member of type T
.
Each declaration achieves three results: (i) it creates and labels a new item in the current scope; (ii) it assigns a type to the item, defining potential values for the item; and; (iii) it may bind a value to the item. If a declaration does not bind a value to an item, the item is considered a variable. If a declaration does bind a value to an item, the item is considered a constant.
An example of a Rosetta declaration that defines an item named mean
is:
mean :: real is 13.5;
In this declaration, mean
is the label of the new item and provides a name used to refer to the item. The type assertion operator assigns a type to the newly declared item. In this example, the type specified is real
, requiring that any value associated with the item must be a real number. The type specification is followed by an optional value constraint specified by the is
clause that binds the item’s value to a constant. In this example, the value of mean is equal to the constant value 13.5
.
The value specified by the is
clause is not an initialization value. The is
clause asserts that the declared item’s value must always be equal to the specified value. Thus, the value of mean
will always be 13.5
in the scope of this declaration. Such declarations are referred to as constant items or simply constants and can be identified by the presence of the is
clause in the declaration.
We define a variable by excluding the is
clause and value from an item declaration. Without this constraint, the value of the item is allowed to vary among all possible elements of its type. For example, the declaration:
variance :: real;
defines an item named variance
whose type is real
, but whose value is not constrained beyond the type in the declaration. When the label variance
appears in the scope of this declaration, it refers to the value associated with variance
. Unlike a constant declaration, the variable item’s value will be constrained elsewhere in the specification, rather than the declaration. Such declarations are referred to as variable items or simply variables and can be identified by the absence of the is
clause in the definition.
A variable or constant may also include a constraint in the form of a where
clause:
mode :: real where (mode > –5.0) and (mode < 5.0);
This declaration specifies that mode
is an item of type real
that must be between the values –5.0
and 5.0
. The item’s value may vary like a variable, but must always satisfy the where
clause predicate.
In general, all Rosetta item declarations take the following form:
items:: T [[ is e ]] [[ where p ]] ;
where items is a comma-separated list of one or more labels, e is an expression, and p is a predicate.
In the item declaration, T constrains the potential values of the items to the contents of a type. If the optional is
clause is present, the new item is a constant item whose value is defined by the expression e. Otherwise, the new item is a variable item. If the optional where
clause is present, then p must hold for all values the item takes on. If the item is constant, the where
clause defines additional properties that must hold for the constant value. If the item is variable, the where
clause defines additional properties beyond those defined by the type.
For example, the function declaration:
triangulate(x,y :: position) :: position;
declares two parameters of type position
that may be used in the function body associated with triangulate
. The declarations create the local parameters and the appropriate type assertions. In parameter declarations, the is
and where
clauses are not allowed.
Similarly, the facet declaration:
facet parity(x::input bit; z::output bit)::state_based is s::bit; begin s' = x xor s; z = s'; end facet parity;
declares two parameters, x
and z
of type bit
, representing component input and output, respectively. Like function parameters, the is
and where
clauses are not allowed in facet parameters. However, a parameter kind that specifies properties is allowed. Here, the input
and output
kinds provide directional information for parameters that will be used as ports. In the declarative region, s::bit
defines a variable item and asserts that it is of type bit
. These declarations are visible throughout the specification body along with the constraint that their values must come from the bit
type.
The function and facet definition forms used thus far are simply syntactic sugar for a traditional declaration and can be expressed using the traditional declaration syntax. The function definition specifies a type constraint using the type assertion operator to define the function’s domain and range elements as type position
. The facet definition contains a type assertion operator asserting the facet is of type state_based
and identifying the domain that forms the basis for this specification. This type assertion can be observed following the facet’s parameter list and is no different than those defining parameters or variables. It asserts that the facet parity is a member of the state_based
facet type.
Example 2.1. Basic declarations
package declaration_examples :: static is w :: integer; x :: real is 2.4; y,z :: string; a :: integer where a>5; b :: real is 2.6 where b > 1.0; end package declaration_examples;
This example package defines six new items and groups them together into a reusable package structure. w
defines a variable item of type integer
while x
defines a constant of type real
whose value is specified as 2.6
. The next declaration defines two variable items, y
and z
, both of type string
. If an is
clause were used in the string declaration, the result would be two constant items with the same value. Specifically:
y,x :: string is "Hello_World";
results in two items whose values are both the constant string "Hello_World"
.
The final two declarations use the where
clause to define constraints on possible values. The declaration of a
is constrained to allow only values greater than 5 while the declaration of b
must be greater than 1.0
. In the latter declaration, the constraint can be verified immediately because b
’s value is known. In the former, the constraint must be placed in the context of another expression to be verified.
The type assertion operator may also be included in expressions when specifiers need to attribute additional type information to an item. In the definition:
z = x::integer + y::integer;
the type assertion operator is used to add type information to an expression, not to declare a new item. If type assertions are omitted:
z = x + y;
the Rosetta type system is used to determine the types of x
and y
. The type assertions tell processing tools to add an assertion that x
and y
are of type integer rather than invoking the type inference system. Such uses of the type assertion relation are simply called type assertions and differ from declarations in that they do not create new items.
Type assertions must be used with care, as nothing prevents assertions from introducing inconsistencies in specifications. However, they are quite useful when an item’s type cannot be inferred automatically, when a prior type inference activity requires documenting, or when the type inference process needs a hint to proceed.
Table 2.2 defines defines operators defined over all Rosetta values and types. Type assertion and subtype and supertype relations support forming type assertions and declarations, and asserting subtype and supertype relationships. Because the type of these relations is boolean
, the use of top
does not introduce problems.
Table 2.2. Operators defined over all Rosetta items
Operator | Syntax | Expression type |
---|---|---|
Type assertion |
|
|
Set membership |
|
|
Subtype and proper subtype |
|
|
Supertype and proper supertype |
|
|
Equality and inequality |
|
|
Equivalence |
|
|
We have seen how the type assertion operator declares items and makes type assertions. Note that the type of the type assertion operator is not boolean
, but is the asserted type. Although this may appear odd, it allows embedding of type assertions in other expressions. For example:
1::integer – 1::real;
is not legal if the type assertion operation is boolean
. Even if it were legal, it does not mean what we intended to say. If the type of the type assertion operation 1::integer
is integer
, then traditional type checking works as expected. Along the same lines, if 1::integer
evaluates to 1
, then evaluation also behaves as expected. Specifically:
1. 1::integer + –1::real 2. == 1 + –1::real 3. == 1 + –1 4. == 0
The set membership operator, x
in
T
, provides the canonical element operation from set theory. The operator is true if its first argument is an element of its second argument. From the user’s perspective, the set membership operation is equivalent to the type assertion operation because types look like sets. In general, types are not sets, but always look like sets to the specifier. Unlike type assertions, the type of the set membership operator is boolean
, allowing it to be used in terms and other boolean statements.
The subtype and supertype relations define relationships among types, again treating types like sets. We will see later that subtype relations correspond with subset relations and supertype relations with superset relations.
Equality and inequality define traditional equality relations. If x=y
is asserted, then the value of x
is the same as the value of y
. This is not the same as assignment, where x
is assigned the value of y
after executing the statement. In the C and C++ programming languages, the following notation would be used to increment the value in variable a
, assuming that a
is an integer
:
a = a + 1;
This expression is inconsistent in Rosetta, as it asserts that the integer value associated with a
is equal to its successor. In a traditional programming language, the semantics of the statement are that the value of a
following statement execution is equal to the value of a
prior to execution plus 1
. Rosetta allows a similar concept when state and state change are a part of the computation model in use. Specifically:
a' = a + 1;
asserts that a
in the next state is equal to a
in the current state plus 1
. Rosetta simply makes the distinction between current and next state visible to the specifier, rather than hiding it in the semantics of assignment. This allows Rosetta specifications to use different next-state definitions based on the computational model being used.
When we make the assertions:
A = 1 (a^2 + 2*a*b + b^2) = (a+b)^2 sqrt(x)*sqrt(x) = x A/= 1
we are defining axioms over the values and items involved. Although this is true for any Rosetta term, mistakes tend to be made when thinking of equality and assignment as the same operation. Rosetta has no assignment operator and thus equality must always be viewed as a relation between values instead of assignment.
Rosetta also provides an equivalence operator that is semantically identical to equality. The distinction is that equivalence binds after virtually all other operations, avoiding confusing parentheses. As an example, consider the following two equivalent terms:
(f(x) = b) = (g(y,z) = d) f(x) = b == g(y,z) = d
Dropping the parentheses from the first term results in a semantics quite different than intended. To see the distinction, we can make the left associativity of equality explicit using parentheses:
(((f(x) = b) = g(y,z)) = d)
Making equality right associative or making equality bind later introduces different problems. However, using equivalence solves the problem. Because equivalence binds last, explicitly parenthesizing the second term above results in exactly the desired semantics:
(f(x) = b) == (g(y,z) = d)
Parentheses can be eliminated because of the low precedence of equivalence. The resulting specification is more intuitive and easy to write. The equivalence relation is used extensively to provide definitions for functions and terms. Throughout this and subsequent chapters, equivalence is used extensively to provide definitions. Rosetta specifications also use equivalence to provide definitions where other mechanisms limit expressiveness.
3.149.228.138