Legend Language
The Legend language is an an immutable functional language based on the Unified Modeling Language (UML) and inspired by Object Constraint Language (OCL). It provides an accelerated data modeling experience that enables:
- Building of logical data models that can help describe business concepts and their data relationships.
- Enrichment of the model with executable constraints, derivations, and model-to-model mappings.
- Execution of model queries in various environments (the Legend language can be transformed into other languages).
Types
The type system for the Legend language is described below. Generics are supported but are reserved for meta-modeling and aren't exposed for use in data modeling.
Primitive types
The following primitive data types are supported. Perform typical arithmetic and logical operations on them for use in writing constraints, derivations, and transformations. Use them to express the data types of attributes in a data model.
- Number (integer, float)
- String
- Boolean
- Date (StrictDate, DateTime)
- The date type is an abstract class with two subtypes:
StrictDate
andDateTime
. A date is automatically cast into one of these two types. - The syntax for
StrictDate
is%YYYY-MM-DD
- The syntax for
DateTime
is%YYYY-MM-DD'T'HH:MM:SS
- The date type is an abstract class with two subtypes:
Class
Create or model a business concept using a class
. Attributes in a class
can be described using the primitive types above.
The following example creates a class
called Firm
with an attribute called name
. The attribute name
is a string with a cardinality of 1.
Class Firm
{
name: String[1];
}
Cardinality (or multiplicity) indicates the range in the size of the set. In the example above, the [1]
indicates that the name
attribute must always be of size 1. It does not indicate the length of the string. Cardinality can also be defined as a range. For example, [0..1]
to show nullability of an attribute, or [2..*]
to show a required minimum size of 2 and an unlimited max.
Enum
Create a set of pre-defined, related values using enum
.
Enum CompanyType
{
Corporation,
LimitedLiabilityCorporation
}
Leverage this enumeration in a class
to define the type of an attribute.
For example, add an attribute called companyType
with a type of CompanyType
to the firm
class created above. Using an enum
restricts and defines the set of values that companyType
attribute can have.
Class Firm
{
name: String[1];
companyType: CompanyType[1];
}
Functions
A subset of the available functions that can aid in the writing of model constraints, derivations, and model-to-model mappings are described below.
A full list of supported functions can be found at Released functions.
Variable
The keyword let
is used to create and assign a variable. Use $
to reference the variable in subsequent statements.
let myVar = 0.75;
let x = $myVar;
Arrow notation
Use the arrow notation to call a function. Reading from left to right, the input (LHS) is being passed into a function (RHS).
$myVar->ceiling();
Lambda
A lambda (anonymous) function is written as {parameter | body with operation}
. A {}
is only required when there's more than one parameter.
myInteger | $myInteger + 33
If, map, and filter
There's no control flow defined in the language. If
, map
and filter
are implemented as functions. Fold
is also available but not supported for data modeling since it's not easy to translate fold
to other languages.
If
Implement an
if
condition with the following syntax.
if ( <!condition>> ,
| <!resultIfTrue>> ,
| <!resultIfFalse>>
);Map
Iterate over a collection using
map
.
$myCollection->map( y| $y+1);Filter
Define a filter using the following syntax.
x | filter($x.name->startsWith('S'))
Extensions
Like UML, the Legend language supports the following extensions: stereotype, profile, and constraint. The language supports an additional extension called derivation (or qualified property). A derivation allows defining an attribute in the data model using functions that can operate on other attributes in the data model.
Stereotype
Class <!extension.important>> Firm
{
name: String[1];
companyType: CompanyType[1];
}
Tagged value
Class <!extension.important>> {doc.doc = 'Represent a company.'} Firm
{
name: String[1];
companyType: CompanyType[1];
}
Constraint
Class <!extension.important>> {doc.doc = 'Represent a company.'} Firm
[
isCorporation: $this.companyType == CompanyType.Corporation
]
{
name: String[1];
companyType: CompanyType[1];
}
Derivation
Class <!extension.important>> {doc.doc = 'Represent a company.'} Firm
[
isCorporation: $this.companyType == CompanyType.Corporation
]
{
name: String[1];
companyType: CompanyType[1];
isCorporation(): {
if($this.companyType == CompanyType.Corporation,
| true,
| false
)
}:Boolean[1];
}
Mappings
The language offers a domain-specific language (DSL) that supports model-to-model mappings in the January pilot release.
Model-to-model mapping
Class NewFirm
{
shortenedCompanyType: String[1];
}
Mapping MyModelToModelMapping
(
NewFirm[newFirmTag] : FirmX
{
~src Firm
shortenedCompanyType: if(
$src.companyType == CompanyType.LimitedLiabilityCorporation,
| 'LLC',
| $src.companyType->toString()
);
}
)