-
Notifications
You must be signed in to change notification settings - Fork 31
JSON Syntax Proposal
There is currently no way to explicitly instantiate a data type. As an example, suppose we want to model an employee of a company.
type Employee:
name string (1..1)
salary number (1..1)
isSeniorMember boolean (1..1)
mentor Employee (0..1)
Currently, the only way to make an instance of this data type is by calling a function that returns an Employee
. Before we can instantiate an Employee
, we therefore must define a constructor function, which is quite verbose, i.e.,
func CreateEmployee:
inputs:
name string (1..1)
salary number (1..1)
isSeniorMember boolean (1..1)
mentor Employee (0..1)
output:
result Employee (1..1)
set result->name: name
set result->salary: salary
set result->isSeniorMember: isSeniorMember
set result->mentor: mentor
With this function, we can instantiate an Employee
by calling CreateEmployee(...)
. We must write such a function each time we want to instantiate some data type.
We intend to introduce an intuitive JSON-like syntax to instantiate a data type. For example, we could instantiate an Employee
called Dwight Schrute who has a mentor called Michael Scott as follows.
Employee {
name: "Dwight Schrute",
salary: 4800.00,
isSeniorMember: False,
mentor: Employee {
name: "Michael Scott",
isSeniorMember: True,
salary: 6300.00,
mentor: empty
}
}
Furthermore, these expressions will be validated automatically by our type system to guarantee correctness, i.e., no attributes were forgotten and each attribute is of the right type.
Note that properties may be written in any order (see the salary
and isSeniorMember
properties of Michael Scott).
In the example above, we used simple literals to set the properties of our employees, but these values may actually be any arbitrary Rosetta expression, e.g.,
Employee {
name: "Dwight" + " " + "Schrute",
salary: GetJuniorSalary(),
isSeniorMember: 45 > 50,
mentor: Employee {
name: "Michael Scott",
isSeniorMember: 55 > 50,
salary: GetSeniorSalary(55),
mentor: empty
}
}
In many Rosetta models such as the CDM, it is common practice to have data types with many optional properties, e.g., with a cardinality constraint (0..1)
. In such cases, it is inconvenient to set most of them to empty explicitly. We will therefore add a shorthand, ...
, which stands for "assign empty to every other attribute". An example taken from the CDM:
type ProductIdentification:
productQualifier productType (0..1)
primaryAssetData AssetClassEnum (0..1)
secondaryAssetData AssetClassEnum (0..*)
externalProductType ExternalProductType (0..*)
productIdentifier ProductIdentifier (0..*)
To instantiate this data type, we could then write
ProductIdentification {
primaryAssetData: AssetClassEnum->Credit,
...
}
which would be equivalent with
ProductIdentification {
primaryAssetData: AssetClassEnum->Credit,
productQualifier: empty,
secondaryAssetData: empty,
externalProductType: empty,
productIdentifier: empty
}
Note that - in a sense - writing ...
is reduntant because Rosetta can easily derive which attributes are absent and automatically set them to empty
. However, the benefit of making this explicit with ...
is that validation can be more thorough and might prevent additional mistakes. Here are two examples that illustrate this.
Suppose we have a type with a list of booleans x
and two optional integers y
and z
.
type A:
x boolean (0..*)
y int (0..1)
z int (0..1)
Suppose we would like to instantiate A
with every attribute set, but that we forgot to set z
, e.g.,
A {
x: [True, False],
y: 5
}
Rosetta wouldn't complain because it assumes that we want to set z
to empty
. However, with the additional ...
syntax, Rosetta would tell me that we were missing the z
property, and we would either have to set z
to a value or add ...
to the end of the expression.
Of course, this is a toy example, but in the CDM it is common practice to have types with many optional properties (more than 10 sometimes), and it becomes easier to forget one. This feature makes it explicit when a user wants to set everything to empty
, and it lets Rosetta help prevent mistakes.
Suppose we have a type with required attributes x
and y
,
type B:
x int (1..1)
y boolean (1..*)
and we have tens or hundreds of expressions such as
B {
x: GetMyFavoriteNumber(),
y: [True, False]
}
written all over the place throughout our model. Suppose now that we want to make a small adjustment to the B
type by adding a list z
, e.g.,
type B:
x int (1..1)
y boolean (1..*)
z int (0..*)
which we typically want to set to some value while instantiating B
. Without the ...
syntax, Rosetta assumes we want to set it to empty and doesn't notify us of any problem. However, with the ...
syntax, Rosetta will complain at every location where we instantiate B
, forcing us to be explicit about setting z
to empty or to some other value.
The following functions do nothing more than constructing data types with given parameters. They could all be removed from the CDM.
Create_PayerReceiver
Create_ExecutionInstruction
Create_ContractFormationInstruction
Create_QuantityChangeInstruction
Create_ClosedState
Create_PrimitiveInstruction
Create_Workflow
Create_Price
Create_PriceQuantity
Create_TradeLot
Create_TradableProduct
Create_Counterparty
Create_LegalAgreementWithPartyReference
The following function calls Create_PrimitiveInstruction
with a lot of empty parameters.
func Create_StockSplit:
inputs:
stockSplitInstruction StockSplitInstruction (1..1)
before TradeState (1..1)
output:
after TradeState (1..1)
…
alias primitiveInstruction:
Create_PrimitiveInstruction(empty, empty, empty, empty, quantityChangeInstruction, empty, empty, empty, empty, empty, empty)
…
With the triple-dot syntax, we could replace this with a more readable and succinct expression.
alias primitiveInstruction:
PrimitiveInstruction {
quantityChange: quantityChangeInstruction,
...
}
Find it on GitHub: https://github.com/vjuge/cdmdsl
Vincent Juge has created an ISDA CDM DSL to ease the development of code utilizing the CDM that also provides a JSON-like syntax to create CDM objects. However, the nature of the ISDA CDM DSL is different.
Using the Rosetta code generator for Java, the CDM is translated into a Java model. Industry users can then use this model to ease the development of their financial application. However, using this generated model directly is verbose in some cases (e.g., to instantiate a generated class such as Trade
using the generated builder pattern). This is where this “CDM DSL” comes in: it automatically generates the boilerplate Java code to instantiate a CDM data type.
In contrast, the present proposal will primarily have an impact for CDM developers (and a minor influence on the Java generator). This proposal acts on how the CDM is developed and how the Java model is generated, whereas Vincent’s DSL acts on how this Java model is consumed.
In terms of dependencies:
- Rosetta is a dependency of the CDM. (The proposal acts here)
- The CDM is a dependency of our generated Java model.
- The generated Java model is a dependency of client applications. (Vincent's DSL acts here)