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

If and how to represent Annotations in M3 #13

Closed
enikao opened this issue Oct 14, 2022 · 9 comments
Closed

If and how to represent Annotations in M3 #13

enikao opened this issue Oct 14, 2022 · 9 comments

Comments

@enikao
Copy link
Contributor

enikao commented Oct 14, 2022

Do we want Annotations, and how to represent them in M3?

See also: #11, #12

What is an annotation?

Limited[1] additional, orthogonal information attached to potentially any node, sharing the node’s lifecycle. The annotated node (or its concept) does not need to know about the annotation.

[1] "limited" because the more complex the annotation is, the more likely it should not be part of the annotated node's lifecycle -- We might want to reuse the complex annotation somewhere else.
Example: In MPS, the editor of a concept can be seen as an annotation of that concept. But even if we deleted the concept, we might want to reuse the editor for another (similar) concept.

Use cases

  • (MPS) Editor of concept: May survive deletion of concept, to be reused for other concept
  • Documentation: Reuse fancy doc language for all other languages
  • Scoper
  • Type system: Makes no sense without the annotated node
  • (MPS) Generator / Antiquotations: Extend original language without original language knowing about annotations
  • View model of editor: Store positions for diagram projection of node
    But: Might want to store somewhere else for version control reasons
  • Target-language specific information, e.g. "For Java, generate to java.math.BigInteger; for C#, generate to System.Numerics.BigInteger

Representation in M3

Alternative A: Just represent them as regular Concepts

Pro:

  • Structurally similar
  • Less concepts on M3

Con:

  • Annotations are always tied to the annotated node; Concepts can be used on their own (or misused if they represent annotations)
  • It can be annoying to filter out annotations from generic processing
  • How to describe annotatable Concepts and multiple flag?

Alternative B: Separate them out into adjacent model

example: EMF ecore vs. genmodel

Pro:

  • Separation of concerns

Con:

  • Never worked well in practice, models eventually get out of sync

Alternative C: First-class citizen in M3, separate from Classifier; don't support containment

classDiagram

class LanguageEntity

class Feature

class Property
Feature <|-- Property

class Classifier
LanguageEntity <|-- Classifier
Classifier "1" *-- "*" Feature: features

class Concept {
  Boolean abstract
  Boolean partition
}
Classifier <|-- Concept
Concept "1" --> "*" Concept: extends
Concept "*" --> "*" ConceptInterface: implements

class ConceptInterface
Classifier <|-- ConceptInterface
%% ConceptInterface"*" o-- "*" ConceptInterface: extends

class Annotation {
  Boolean multiple
}
Annotation "*" --> "1" Classifier: annotates
LanguageEntity <|-- Annotation
Annotation "1" *-- "*" Property: properties
Annotation "1" --> "*" Annotation: extends
Annotation "*" --> "*" ConceptInterface: implements

Pro:

  • Clearly separates them from regular concepts
  • Structurally enforces node.annotations can only be of Annotation type
  • Compatible with EMF annotations

Con:

  • Would need to replicate a lot of Classifier structure, e.g. supertype, features
  • Annotations in most systems (Java, C#, MPS) support containment-like structure

Alternative D: First-class citizen in M3, subtype of Classifier

classDiagram

class LanguageEntity

class Feature

class Classifier
LanguageEntity <|-- Classifier
Classifier "1" *-- "*" Feature: features

class Concept {
  Boolean abstract
  Boolean partition
}
Classifier <|-- Concept
Concept "1" --> "*" Concept: extends
Concept "*" --> "*" ConceptInterface: implements

class ConceptInterface
Classifier <|-- ConceptInterface
%% ConceptInterface"*" o-- "*" ConceptInterface: extends

class Annotation {
  Boolean multiple
}
Annotation "*" --> "1" Classifier: annotates
Classifier <|-- Annotation
Annotation "1" --> "*" Annotation: extends
Annotation "*" --> "*" ConceptInterface: implements

Pro:

  • Leverages similarity and capabilities to Classifier

Con:

  • Introduces non-structural constraint on node.annotations return type?? Don't understand that
  • Duplicates structures of Concept

Alternative E: Specialization of Concept

classDiagram

class LanguageEntity

class Feature

class Classifier
LanguageEntity <|-- Classifier
Classifier "1" *-- "*" Feature: features

class Concept {
  Boolean abstract
  Boolean partition
}
Classifier <|-- Concept
Concept "1" --> "*" Concept: extends
Concept "*" --> "*" ConceptInterface: implements

class ConceptInterface
Classifier <|-- ConceptInterface
%% ConceptInterface"*" o-- "*" ConceptInterface: extends

class Annotation {
  Boolean multiple
}
Annotation "*" --> "1" Classifier: annotates
Concept <|-- Annotation

Pro:

  • Leverages similarity and capabilities to Concept

Con:

  • Annotation can never be a partition, so inherited partition flag is useless
  • What is an abstract annotation?
  • Non-structural constraint: Annotation and Concept can only extend their own kind OR we allow that mixture.
    The mixture is weird if used but not a structural problem.

Alternative F: Marker interface in builtin library

Does not work, as the concept that implements IAnnotation would need to instantiate it, i.e. fill IAnnotation's features with concrete values.

classDiagram

class IAnnotation ["builtins::IAnnotation"] {
  Boolean multiple
}
<<ConceptInterface>> IAnnotation
IAnnotation "*" --> "1" Classifier: annotates

class LanguageEntity

class Feature

class Classifier
LanguageEntity <|-- Classifier
Classifier "1" *-- "*" Feature: features

class Concept {
  Boolean abstract
  Boolean partition
}
Classifier <|-- Concept
Concept "1" --> "*" Concept: extends
Concept "*" --> "*" ConceptInterface: implements

class ConceptInterface
Classifier <|-- ConceptInterface
%% ConceptInterface"*" o-- "*" ConceptInterface: extends

Pro:

  • No change to M3

Con:

  • Annotations (i.e. Concepts implementing IAnnotation) can appear everywhere, even in regular structure

Alternative G: Separate M3 language, similar to derived model

Annotations are “just” a derived model (as e.g. type system). Each client can request them the same way as any other derived model.

Difference: Annotations are stored, type system is calculated.
This would break our assumption: “Derived model can be reconstructed from original model”.

Pro

  • Separation of concerns
  • No change to M3

Con:

  • More builtin languages to deal with
  • Update issues
    • How to delete annotations alongside with annotated node?
      --> Would need an active repository, or a processor
    • Where to add annotation for a specific annotated node, e.g. what's the annotation's parent?

This could be implemented as a “adjacent annotation” processor implementing annotations as separate models

@ftomassetti
Copy link
Contributor

ftomassetti commented Oct 14, 2022

I understood that the non-structural constrain to be introduced would be Link.type, not node.annotations

Is it right that we need to introduce these constraints?

  • Annotation.target is Structurally an AbstractConcept but it cannot be Annotation
  • Link.type is Structurally an AbstractConcept but it cannot be Annotation

@enikao
Copy link
Contributor Author

enikao commented Oct 14, 2022

Annotation.target is Structurally an AbstractConcept but it cannot be Annotation

I think yes

Link.type is Structurally an AbstractConcept but it cannot be Annotation

I'd say Containment.type can never be Annotation.
What about Reference.type? Can one Annotation refer to another one? Can we refer from a Concept that's a child of an Annotation to another Annotation?

@ftomassetti
Copy link
Contributor

Good point. I guess we could allow references to Annotation, but I have not a strong opinion on this and I would be curious to hear what others think

@dslmeinte
Copy link
Contributor

I think annotations are quite different beasts from the other subtypes of FeaturesContainer (see issue #20 as well). We also would need to think of the semantics: how can you instantiate them?
So yes: we should make constraints that exclude annotations as Link.type.

@markusvoelter
Copy link
Contributor

.... and otherwise make them concepts, right?

@ftomassetti ftomassetti added the M3 label Nov 4, 2022
@ftomassetti
Copy link
Contributor

ftomassetti commented Nov 4, 2022

Have we concluded that Annotation cannot be used as the target of references?
Should we mark this issue as closed?

@enikao enikao added Annotation postponed Postponed for later discussion labels Nov 25, 2022
@enikao enikao removed the postponed Postponed for later discussion label Jul 10, 2023
@joswarmer
Copy link
Contributor

I think there is little reason for annotations to be defined at the M3 level. Annotations can simply be defined in a separate language at the M2 level. Annotations are then just concepts, like any other concepts and they can use inheritance, interfaces etc just like in any other language.
Use cases range from language specific to generic annotations and from simple to complex annotations:

  • Use case for a "Documentation Annotation" language. You define a Documentation Language where a DocumentationConcept has a reference to a Node. This way you can define documentation annotations attached to every node in any other language.
  • Another use case is e.g. a Java Annotation Language, which is defined for a specific Language X. Her we can define annotation concepts with references to specific concepts in the X language that can be annotated.
  • An extension to the Java Annotation Language is e.g. to define a TypeScript Annotation language that defined annotation concept also to refer to concept in language X. Thus way, users of language X can decide whether they want to use Java or TypeScript or both annotation languages (or none at all).
  • This allows annotations on annotations. In the examples above, you can e.g. use a documentation annotation referring to Java annotation.
  • Another use case can be found in e.g. Freon. An editor definition for a concept in a language is defined using an editor- concept that refers to a concept in the meta-model. The complete "editor language" is in essence a set of editor annotations that refer to concepts in the meta-model and describe how they should be shown in an editor.
  • Essentially, all aspects in an MPS language definition, except for the structure aspect, are annotations on concepts defined in the language structure. This is similar to Freon.

The reference in this solution is always from the annotation to the annotated concept, which I think is conceptually correct.

Whether we expect annotations to show in an editor together with the annotated concept or separately is purely an editor/tool issue. Both options can be supported, even within the same editor/tool. In the use cases above both options are being used.

I see no benefit from creating a separate M3 notion of Annotation because:

  • Adding annotations at M3 level makes the M3 model more complex
  • Annotations at the M3 level need either duplication or a set of constraints to ensure that they work correctly.
  • Annotations at the M3 level are very limited, while using separate Annotation Languages (at the M2 level) make the use of annotations much more flexible and powerful.

The solution of having separate annotation languages at the M2 level is a combination of alternative A (represent annotations as concepts) and alternative B (separate annotations out in a separate model).

The "con" at alternative B that it did not work out in practice is disputable. At Mendix we used a separate annotation model for defining version annotations attached to concepts, which worked very well. And in Freon we also use separate annotation model for editor definitions etc., which also works fine.

Gives this I am strongly in favor of not adding Annotation to the M3 level.

@enikao
Copy link
Contributor Author

enikao commented Jul 13, 2023

The reference in this solution is always from the annotation to the annotated concept, which I think is conceptually correct.

If I understand correctly, this is on M2 level: The AnnotationConcept (e.g. DocumentationAnnotation) refers to annotatable concepts, e.g. JavaClass or TypescriptMethod.

On M1 level, we need the opposite direction: For each JavaClass instance, I need to find all attached annotation instances. So we need to somehow represent them in serialization (details tbd).
(This does not contradict above's finding, just clarification).

The "con" at alternative B that it did not work out in practice is disputable.

The quoted con argument also relates to M1: In GMF, it was very hard to keep the "annotations" to ecore instances (kept in genmodel, gmfgraph, gmftool, ... models) in sync.

@enikao
Copy link
Contributor Author

enikao commented Aug 4, 2023

Decision via Slack vote on 2023-08-04: Alternative D: First-class citizen in M3, subtype of Classifier

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants