Skip to content

Discrepancy between generic parameters declaration and references #216

Open
@swallez

Description

@swallez

There is a discrepancy in metamodel.ts between how we declare the generic parameters of a type, and how they are later referenced in its definition:

  • declaration uses generics: string[]
  • usage in the definition uses a TypeName like for any other type, consisting of a name and a namespace. For open generic parameters references, the name is the one used in the declaration, which is expected, but the namespace is the one of the enclosing type, which is more surprising.

Example:

{
  "kind": "interface",
  "name": {
    "name": "KeyedBucket",
    "namespace": "aggregations"
  },
  "generics": [
    "TKey"
  ],
  "properties": [
  {
    "name": "key",
    "required": true,
    "type": {
      "kind": "instance_of",
      "type": {
        "name": "TKey",
        "namespace": "aggregations"
      }
    }
  },
  ...

The fact that declarations use a simple name and references use a namespace that depends on the current type makes processing generic parameters more complicated than needed.

To resolve this inconsistency, we can consider different approaches:

  • change the declaration to also use namespaces: generics: TypeName with the same namespace as the one used when they are referenced:

    {
      "kind": "interface",
      "name": {
        "name": "KeyedBucket",
        "namespace": "aggregations"
      },
      "generics": [
        {
          "name": "TKey",             // <------
          "namespace": "aggregations" // <------
        }
      ],
      "properties": [
      {
        "name": "key",
        "required": true,
        "type": {
          "kind": "instance_of",
          "type": {
            "name": "TKey",
            "namespace": "aggregations"
          }
        }
      },
      ...
  • keep the generics? : string[] declaration and consider that open generic parameters all live in a builtin generic_parameters namespace. References would then use that namespace:

    {
      "kind": "interface",
      "name": {
        "name": "KeyedBucket",
        "namespace": "aggregations"
      },
      "generics": [
        "TKey"
      ],
      "properties": [
      {
        "name": "key",
        "required": true,
        "type": {
          "kind": "instance_of",
          "type": {
            "name": "TKey",
            "namespace": "generic_parameters" // <------
          }
        }
      },
      ...

The first approach makes the model more self-contained (no magic builtin namespace) but requires code generators to keep track of type names that are open generic parameters in the enclosing scope, while the second approach introduces a convention but avoids code generators to build that open generics context.

A 3rd approach could be to combine the two, using full type names for declarations so that the model is internally consisitent, but making sure (and validating in validate-model.ts) that open generics live in the generic_parameters namespace so that code generators can just match on that namespace to distinguish them:

{
  "kind": "interface",
  "name": {
    "name": "KeyedBucket",
    "namespace": "aggregations"
  },
  "generics": [
    {
      "name": "TKey",                   // <------
      "namespace": "generic_parameters" // <------
    }
  ],
  "properties": [
  {
    "name": "key",
    "required": true,
    "type": {
      "kind": "instance_of",
      "type": {
        "name": "TKey",
        "namespace": "generic_parameters" // <------
      }
    }
  },
  ...

Thoughts?

/cc @Mpdreamz @delvedor @stevejgordon

Note: TypeAlias generics use a ValueOf[]. This is a bug in the model and it should use the same approach as other type declarations.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions