This cookbook should give you quick answers and guidelines when designing types for your languages. For in-depth description of the typesystem please refer to the Typesystem section of the user guide. For usages of the type system, please refer to the Using the type-system page.
Use type equation when the type of a node should always be a particular concrete type. Use the typeof command to declare that the type of the desired node should equal to a particular type.
Note quotation is used to refer to a type. <string> is equivalent to typing new node<StringType>(). The type of an element is equal to the type of some other element. For example, the to express that parentheses preserve the type of the wrapped element, the ParenthesizedExpression concept declares:
When the types should be sub-types or super-types of other types, use the infer typeof command. See the ternary operator as an example:
The ForEachStatement concept illustrates how to solve quite an involved scenario. The type of the loop variable must be equal to the type of elements in the iterated collection, while the type of the collection must be a sub-type of either a sequence or an array of elements of the elementType type.
Notice, we use var elementType to declare a variable, which we then use to tie together the type of the collection elements and the type of the loop variable. Also, %(...)% demarcates a so called anti-quotation, which allows you to provide values from your local context into the AST you are manipulating or retrieve them back.
Replacement rules indicate to the type system the possibility to replace one type with another. For example, NullType is a subtype of all types (except for primitive types) and so the type system can simply remove the inequation between NullType and BaseConcept.
Replacement rules are also handy to declare covariance and contravariance. For example, covariance for sequences is declared in MPS as follows:
The original rule claiming that the left collection is a subtype of the right collection gets replaced with a rule ensuring that the type of elements in the left collection is a subtype of the type of elements in the right collection.
Subtyping rules allow you to specify where the particular type belongs in the type hierarchy. The rule returns a collection of types, which it identifies as its direct super-types. The following rule, for example, declares that Long variables can be cast to Float.
Here MPS declares, that LinkedList is a subtype of either a List, a Deque or a Stack:
When two types should be interchangeable, use comparison rules to define that. For example, the following rule makes NullType comparable with any type, except for primitive ones:
Similarly, the MapType from BaseLanguage and the Map interface from Java (here refered to through the ClassifierType concept inside a pattern) should be comparable:
Checking and Quick-fixes
Checking rules become part of the MPS code analysis process and will report found issues to the user interactively in the editor. For example, this is a check for superfluous type casts:
Now you can define a quick-fix that will pop-up to the user whenever the problem above is reported. The user can then quickly invoke the quick-fix to correct the reported issue.
When-concrete, overloaded operations
When-concrete blocks allow you to perform type checks once the type a node has been calculated. In the example below we are checking, that the calculated type of an operation matches the type suggested by the operation type command based on the operator overriding rules: