Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value Types #265

Open
enikao opened this issue May 18, 2024 · 3 comments
Open

Value Types #265

enikao opened this issue May 18, 2024 · 3 comments
Labels
2024.1 LionWeb release 2024.1 M3 serialization

Comments

@enikao
Copy link
Contributor

enikao commented May 18, 2024

We could introduce value types in LionWeb.

Characteristics

Value types:

  • have no identity
  • assignment implies copy-by-value
  • are composed of fields
  • tend to be immutable
  • tend not to support inheritance
  • tend to support interface implementation
  • are meant to support a small composite of values that semantically form a unit, like "Complex number { real part, imaginary part }" or "Color { red, green, blue }".

Support in programming languages

Language mutability inheritance can implement interfaces/traits allowed member types
C#: struct types optional: readonly struct no yes any
Java: Preview of value classes immutable no yes any
Kotlin: currently inline classes (support only one member), plans for value classes; alternatively Data Classes immutable / optional no / data classes can extend other classes yes / yes any / yes
Scala 2: value classes, Scala 3: opaque types (both support only one member) probably immutable no yes probably any
Rust: struct type mostly immutable (orthogonal aspect) no (not a thing for Rust) yes any
C++ struct type (exactly same as class with different default visibility) mutable multiple n/a any
Go: struct type mutable kind-of (embedding) yes some limitations on direct and indirect member types
Swift: struct type mutable no yes probably any
Typescript / Javascript: Not supported

Proposal for LionWeb: Value types as structured datatypes

A language can define a structured datatype (name tbd). Its a specialization of Datatype, thus can be used as type of a Property.

A structured datatype consists of one or more fields (name tbd). Each field has a name, key (inherited from IKeyed), and type. type is a reference to a Datatype. In other words: A structured datatype is a named tuple of other datatypes.

It can NOT contain Classifiers.
Rationale: we have a strict separation between identified things (nodes) and values. If we allowed nodes inside structured datatypes, we can have nodes nested inside the value-world of properties.

Structured datatypes can NOT inherit from anything, and can NOT implement interfaces.
Rationale: inheritance seems widely not supported by programming languages. It also clashes with the idea of "small composite of values". Technically I don't see a reason why we should not allow inheritance (besides programming language support).
All listed programming languages allow interface implementation, but for behavior only. We don't have behavior at all in LionWeb, thus allowing to implement interfaces makes no sense. Also, interfaces might have Link features which are not allowed in a structured datatype.
We could support value interfaces like Kotlin if we wanted to group similar structured datatypes. This seems again at odds with the "small composite of values" idea -- if we have to group them, they aren't that small any more.

Structured datatypes CAN have a field with themselves as type.
Rationale: With value semantics, and no possibility to reference structured datatypes, I don't see an issue with nesting.
We cannot pre-calculate their size, but neither can we for String values.

All fields are required.
Rationale: Simplification, adhere to "small composite of values" idea.

I don't think we have to make a statement about mutability, other than NOT supporting delta updates of parts of a structured datatype Property.
Rationale: LionWeb is a protocol, and thus at its core only care about data in transit. During one transit, no data changes ever.
Assume we have a Structured Datatype Color { red: Integer , green: Integer, blue: Integer } and a Concept Apple { property color: Color }. Assume we write myApple.color.red = 255 in our client's code, implemented in some programming language.
Whether the programming language changes the existing Color value, or creates a new copy of the previous value with updated red component, is not relevant to LionWeb.
We could state the assumption that structured datatypes are immutable to encourage more uniform implementations, and enable optimizations (e.g. de-duplication).

We serialize structured datatype instances as strings, the same as any other Datatype. the string contains a JSON structure corresponding to the structured datatype, using the field key as JSON member key, and the instance's field value as JSON member value. We omit the string escaping for nested structured datatypes.
Rationale: We already support JSON datatypes.
Using a field's key instead of its name is in line with the rest of serialization, and avoids issues with renaming.
We don't want to add one level of string escaping for every nesting of structured datatypes, as this would be very error-prone to parse and extremely tedious to read or write by a human.

Example 1: Apple with color

M2:

Language Ex1 [key-ex1] version: "1"

structured datatype Color [key-color]
  red: Integer [key-color-red]
  green: Integer [key-color-green]
  blue: Integer [key-color-blue]

concept Apple [key-apple]
  property expectedColor: Color [key-apple-expectedColor]

M1:

{
  "id": "123",
  "classifier": { "language": "key-ex1", "version": "1", "key": "key-apple" },
  "properties": [
    {
      "property": { "language": "key-ex1", "version": "1", "key": "key-apple-expectedColor" },
      "value": "{ \"key-color-red\": \"255\", \"key-color-green\": \"128\", \"key-color-blue\": \"64\" }"
    }
  ],
  "containments": [],
  "references": [],
  "annotations": [],
  "parent": null
}

Example 2: Complex number with Decimal

M2:

Language Ex2 [key-ex2] version: "1"

structured datatype Decimal [key-decimal]
  int: Integer [key-decimal-int]
  frac: Integer [key-decimal-frac]

structured datatype ComplexNumber [key-compex]
  real: Decimal [key-complex-real]
  imaginary: Decimal [key-complex-imaginary]

concept SquareRootResult [key-sqrtres]
  property input: Integer [key-sqrtres-input]
  property result: ComplexNumber [key-sqrtres-result]

M1:
(note: the newlines inside key-sqrtres-result value serve readability, they would be omitted.)

{
  "id": "234",
  "classifier": { "language": "key-ex2", "version": "1", "key": "key-sqrtres" },
  "properties": [
    {
      "property": { "language": "key-ex2", "version": "1", "key": "key-sqrtres-input" },
      "value": "-1"
    },
    {
      "property": { "language": "key-ex2", "version": "1", "key": "key-sqrtres-result" },
      "value": "{ 
        \"key-complex-real\": { \"int\": \"0\", \"frac\": \"0\" },
        \"key-complex-imaginary\": { \"int\": \"1\", \"frac\": \"0\" }
      }"
    }
  ],
  "containments": [],
  "references": [],
  "annotations": [],
  "parent": null
}

Questions

  • Is field actually the same as Property? Can we re-use Property there? The only difference is the required flag of Properties (would always be true for fields).
@enikao
Copy link
Contributor Author

enikao commented May 25, 2024

Fields with a primitive datatype or enum type are required and cannot be null. Fields with a (nested) structured datatype type can be null.

Rationale: Simplification, adhere to "small composite of values" idea.
We want to allow a structured datatype to nest itself, so at some point this field MUST be null, otherwise we require infinite nesting.

@enikao
Copy link
Contributor Author

enikao commented May 25, 2024

For fields of enum type, we store the enum literal's key (#128). This key might be specific to a version of the hosting language. We always know this version, because it is mentioned in the definition of the structured datatype.

@enikao
Copy link
Contributor Author

enikao commented May 25, 2024

We do not reuse M3 Property to declare structured datatype fields. We add a new entity Field to M3.

Rationale: Property inherits optional from Feature, but Field is never optional (except for nested structured datatypes, see above). Also, semantically Property and Field are different things, they just happen to look similar. Furthermore, it might be confusing to have slightly different serializers for Property inside a Classifier vs. inside a structured datatype.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2024.1 LionWeb release 2024.1 M3 serialization
Projects
None yet
Development

No branches or pull requests

1 participant