Skip to content

JSON Syntax Proposal

SimonCockx edited this page Sep 9, 2022 · 8 revisions

Proposal: JSON-like syntax for instantiating data types

Problem description

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.

Proposed solution

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
  }
} 

Additional triple-dot syntax for assigning empty to all absent attributes

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.

1. Setting attributes to empty implicitly might be unintentional.

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.

2. Implicitly setting every absent attribute to empty can be problematic during refactoring.

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.

Direct implications for the CDM

Constructors that could be removed

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

Better interface for sparse data types

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, 
            ... 
        } 

Relation to the “ISDA CDM DSL” of Vincent Juge

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)