On the Use of an Internal DSL for Enriching EMF Models
1. On the Use of an Internal DSL
for Enriching EMF Models
Filip Křikava Philippe Collet
Université Nice Sophia Antipolis
I3S - CNRS UMR 7271
France
2012 Workshop on OCL and Textual Modeling (OCL 2012), MODELS’12
September, 30th, 2012 - Innsbruck/AUSTRIA
3. The Journey
- applying MDE into Self-Adaptive Software
Systems
Meta-modeling with EMF
2
4. The Journey
- applying MDE into Self-Adaptive Software
Systems
Meta-modeling with EMF
- limited expressiveness of structural constraints
Enriching EMF with OCL
2
5. The Journey
- applying MDE into Self-Adaptive Software
Systems
Meta-modeling with EMF
- limited expressiveness of structural constraints
constraints Enriching EMF with OCL
operation contracts
implementation of
operation bodies
implementation of
derived features
2
6. The Journey
- applying MDE into Self-Adaptive Software
Systems
Meta-modeling with EMF
- limited expressiveness of structural constraints
constraints Enriching EMF with OCL
operation contracts - encountered number of shortcomings
- since our work is based on Scala
implementation of
operation bodies
Enriching EMF with Scala
implementation of
derived features
2
7. The Journey
- applying MDE into Self-Adaptive Software
Systems
Meta-modeling with EMF
- limited expressiveness of structural constraints
constraints Enriching EMF with OCL
operation contracts - encountered number of shortcomings
- since our work is based on Scala
implementation of
operation bodies
Enriching EMF with Scala
implementation of - internal DSL in Scala to enrich EMF
derived features - similar OCL features
- full advantage of the host language
- state-of-the-art tool support
+
2
9. Some Shortcomings of OCL
• Based solely on our experience
• different usage of OCL - different pros/cons
• concerned by practicality rather than formality
• not a complete study nor first one (well known issues)
4
10. Some Shortcomings of OCL
Expressions
Constraints Capturing
OCL Expressions Tool Support
Constructs
• Poor support for user
feedback
• Complex expressions are • No support for warning / • Scalability and stability
hard to write and critiques problems with larger
maintain models
• No support for
• Limited extensibility and dependent constraints • Limited developer
reuse feedback
• Limited flexibility in
• Side-effect free context definition • Insufficient debugging
expressions capability
• No support for repairing
• Inconsistency/Confusing inconsistencies
issues • No support for invariant
reuse across different
models
D. Kolovos, et al. On the Evolution of OCL for
Capturing Structural Constraints, In Rigorous
Methods for Software Construction and
Analysis, 2009. 5
11. Internal DSL Approach - Overview
• The shortcomings are related to scalability
• OCL language
• performance, stability and features of OCL tools
• when used in the large (many invariants, complex
expressions)
• similar problems are in other external DSLs (EOL/EVL)
6
12. Internal DSL Approach - Overview
• External DSL like OCL raises the level of abstraction:
7
13. Internal DSL Approach - Overview
• External DSL like OCL raises the level of abstraction:
parent.types
->select(name <> '')
->forAll(e | e <> self implies e.name <> self.name)
7
14. Internal DSL Approach - Overview
• External DSL like OCL raises the level of abstraction:
parent.types
->select(name <> '')
->forAll(e | e <> self implies e.name <> self.name)
• The same could be expressed in plain Java, but ... no
7
15. Internal DSL Approach - Overview
• External DSL like OCL raises the level of abstraction:
parent.types
->select(name <> '')
->forAll(e | e <> self implies e.name <> self.name)
• The same could be expressed in plain Java, but ... no
• Having a language that has a more powerful and
flexible constructs and state-of-the-art tool support
7
16. Internal DSL Approach - Overview
• External DSL like OCL raises the level of abstraction:
parent.types
->select(name <> '')
->forAll(e | e <> self implies e.name <> self.name)
• The same could be expressed in plain Java, but ... no
• Having a language that has a more powerful and
flexible constructs and state-of-the-art tool support
self.parent.types
.filter(name != '')
.forall(e => e != self implies e.name != self.name)
7
17. Internal DSL Approach - Why Scala?
• Modern general purpose language
• Runs on top of JVM
• Well designed for building internal DSLs
• Statically typed using type inference
• Well supported by major tool vendor
8
34. Internal DSL Approach - Basics
• Constraints
def validateOfAge(self: Customer): Boolean = self.age >= 18
def validateOfAge(self: Customer): Option[String] =
if (self.age >= 18) {
None
else {
Some("The person %s is under age" format self.name)
}
11
35. Internal DSL Approach - Basics
• Constraints
def validateOfAge(self: Customer): Boolean = self.age >= 18
def validateOfAge(self: Customer): Option[String] =
if (self.age >= 18) {
None
else {
Some("The person %s is under age" format self.name)
}
11
36. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
12
37. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
def validateDefinesGetInstance(self: UMLClazz) = {
}
13
38. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
def validateDefinesGetInstance(self: UMLClazz) = {
self.features.find(_.name == "getInstance") match {
}}
14
39. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
def validateDefinesGetInstance(self: UMLClazz) = {
self.features.find(_.name == "getInstance") match {
case Some(_) => Success
}}
15
40. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
def validateDefinesGetInstance(self: UMLClazz) = {
self.features.find(_.name == "getInstance") match {
case Some(_) => Success
case None => Error("Missing getInstance"
)}}
16
41. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
def validateDefinesGetInstance(self: UMLClazz) = {
self.features.find(_.name == "getInstance") match {
case Some(_) => Success
case None => Error("Missing getInstance",
QuickFix("Add a getInstance operation", {
}
))}}
17
42. Internal DSL Approach - Basics
• Constraints
• a constraint that check whether a UML class contains
getInstance method
def validateDefinesGetInstance(self: UMLClazz) = {
self.features.find(_.name == "getInstance") match {
case Some(_) => Success
case None => Error("Missing getInstance",
QuickFix("Add a getInstance operation", {
clazz: UMLClazz =>
clazz.features += create[UMLFeature] { op =>
op.setName("getInstance")
// ...
}
}
))}}
18
43. Internal DSL Approach - Basics
• Constraints dependency
def validateDefinesGetInstance(self: UMLClazz)
@satisfies(“DefinesGetInstance”)
def validateDefinesGetIsStatic(self: UMLClazz)
• Using proxy to ensure each invariant is called at most
once for a specific instance
• Referencing by strings is not very practical
• Looking into Scala 2.10 macros
19
44. Internal DSL Approach - Basics
• Constraints dependency
def validateDefinesGetInstance(self: UMLClazz)
@satisfies(“DefinesGetInstance”)
def validateDefinesGetIsStatic(self: UMLClazz)
• Using proxy to ensure each invariant is called at most
once for a specific instance
• Referencing by strings is not very practical
• Looking into Scala 2.10 macros
19
45. Internal DSL Approach - Extensibility
• Scala is extensible language
• For example, there is no implies operation in Scala
self.parent.types
.forall(e => e != self implies e.name != self.name)
20
46. Internal DSL Approach - Extensibility
• Scala is extensible language
• For example, there is no implies operation in Scala
self.parent.types
.forall(e => e != self implies e.name != self.name)
• Possibility to extend existing types
class ExtBoolean(a: Boolean) {
def implies(b: => Boolean) = !a || b
}
implicit def extBoolean(a: Boolean) = new ExtBoolean(a)
20
47. Internal DSL Approach - Extensibility
• Scala is extensible language
• For example, there is no implies operation in Scala
self.parent.types
.forall(e => e != self implies e.name != self.name)
• Possibility to extend existing types
class ExtBoolean(a: Boolean) {
def implies(b: => Boolean) = !a || b
}
implicit def extBoolean(a: Boolean) = new ExtBoolean(a)
• This way we can add missing OCL operations (closure) 20
48. Internal DSL Approach - Reusability
• Breaking expression into smaller pieces
• Organizing them as functions in objects and libraries
• Shared across project
21
49. Internal DSL Approach - Reusability
• Breaking expression into smaller pieces
• Organizing them as functions in objects and libraries
• Shared across project
• Push further using structural typing - reused across
models
def validateNonEmptyName(self: { def name: String } ) =
!self.name.isEmpty
21
50. Internal DSL Approach - Undefined
and Invalid Values
interface Person {
Person
public String getFirstName();
- firstName : String [0..1]
- lastName : String [1..1]
public String getLastName();
}
• Using the NonEmptyName constraint might lead to NPE
!self.firstName.isEmpty
22
51. Internal DSL Approach - Undefined
and Invalid Values
interface Person {
Person
public String getFirstName();
- firstName : String [0..1]
- lastName : String [1..1]
public String getLastName();
}
• Using the NonEmptyName constraint might lead to NPE
!self.firstName.isEmpty
self.firstName != null implies !self.firstName.isEmpty
22
52. Internal DSL Approach - Undefined
and Invalid Values
interface Person {
Person
public String getFirstName();
- firstName : String [0..1]
- lastName : String [1..1]
public String getLastName();
}
• Using the NonEmptyName constraint might lead to NPE
!self.firstName.isEmpty
self.firstName != null implies !self.firstName.isEmpty
22
53. Internal DSL Approach - Undefined
and Invalid Values
interface Person {
Person public scala.Option<String>
- firstName : String [0..1] getFirstName();
- lastName : String [1..1] public String getLastName();
}
Option<T>
- get() : T
- isDefined : Boolean
… … ...
Some None
23
54. Internal DSL Approach - Undefined
and Invalid Values
interface Person {
Person public scala.Option<String>
- firstName : String [0..1] getFirstName();
- lastName : String [1..1] public String getLastName();
}
Option<T>
- get() : T
- isDefined : Boolean
… … ...
Some None
23
55. Internal DSL Approach - Type Casts
• OCL
if self.oclIsKindOf(Customer) then
self.oclAsType(Customer).someAction()
else
// something else
endif
• Scala
self match {
case c: Customer => c.someAction()
case _ => // something else
}
24
56. Internal DSL Approach - Drawbacks
• Expressions can contain arbitrary code
• difficult to ensure side-effect free expressions
• possibility to use external checker like IGJ
• @ReadOnly parameter annotation
• Loss of formal reasoning and analysis
• No constructs related to postconditions
25
57. Implementation - Sigma Framework
• Simple API for enriching EMF models using
Java class methods - delegate computation
• Small layer on top of EMF and EValidator
• Target language agnostic (Xtend, Kotlin, ...)
• Ecore
EMF
• EValidator
Sigma Core
Sigma Scala
Codegen Dynamic • generate direct calls
EMF Delegates Templates
• reduce the runtime
overhead caused by
reflection
• simplifies
debugging,
code orientation
26
58. Conclusion
• Improve the EMF integration and solid tools to
the community
• Specify the deviations from OCL and its
consequences
• Specify the trade-offs between standardized
OCL and flexibility of the DSL approach
• Looking beyond traditional usage of OCL
27
59. The Journey further on
... ... ...
+
Model to Text Model to Model Model Testing
transformation transformation
28
60. Thank you!
http://nyx.unice.fr/projects/sigma
Filip Křikava
filip.krikava@i3s.unice.fr
The work reported in this paper is partly funded by the ANR SALTY project
under contract ANR-09-SEGI-012.
29
61. Related Work
• There is a plenty of work addressing OCL
shortcomings - popular research subject
• OCLLib, OCLUnit, OCLDoc, ...
• Epsilon project
• JS4EMF
• Xcore
• “C# 3.0 makes OCL redundant”
30
62. Some Shortcomings of OCL
Expressions
• Side-effect free expressions
• by default, expressions are side-effect
• no assignment semantics
• all data-types are immutable
• useful for constraints
• impossible to implement state changing operations
• no support for generic class type parameters
• while having generic collections
31
63. Some Shortcomings of OCL
Expressions
• Some inconsistency, confusions
• ‘.’ and ‘->’ operators
• implicit oclAsSet
• implicit collection flattening in collect
• logical operation with undefined values
• different OCL meta-models
• no support for generic class type parameters
• while having generic collections
32
64. Internal DSL Approach - Principles
• EMF codegen translates model concepts into Java
• model classifier -> Java class
• structural / behavioral feature -> method
33
65. Internal DSL Approach - Principles
• EMF codegen translates model concepts into Java
• model classifier -> Java class
• structural / behavioral feature -> method
• Scala allow to omit parenthesis in zero arg methods
self.getMembership.getParticipant.getDateOfBirth
33
66. Internal DSL Approach - Principles
• EMF codegen translates model concepts into Java
• model classifier -> Java class
• structural / behavioral feature -> method
• Scala allow to omit parenthesis in zero arg methods
self.getMembership.getParticipant.getDateOfBirth
• Remove get noise using codegen dynamic templates
self.membership.participant.dateOfBirth
33
67. Internal DSL Approach - Principles
• EMF codegen translates model concepts into Java
• model classifier -> Java class
• structural / behavioral feature -> method
• Scala allow to omit parenthesis in zero arg methods
self.getMembership.getParticipant.getDateOfBirth
• Remove get noise using codegen dynamic templates
self.membership.participant.dateOfBirth
• Scala supports lambda expressions
• Large number of collection operations
33
68. Some Shortcomings of OCL
Expressions
• Complex expressions are hard to write and maintain
• linearity of the OCL expression
• Limited extensibility and reuse
• difficult to add new operations (i.e. support for regular expressions)
• Side-effect free expressions
• by default side-effect free expressions; no instantiation
• Inconsistency/Confusing issues
• implicit flattening, logic with undefined values, missing generics class
type parameters, etc.
34
69. Some Shortcomings of Tool Support
• Impressive number of academic OCL tools
• lack of commercial tools
• Eclipse OCL project
• Set of OCL editors (e.g. OCLInEcore)
• Based on EMF delegates
• Scalability and stability problems with larger models
• Limited developer feedback
• Insufficient debugging capability
35
70. Some Shortcomings of OCL
Expressions
“... Second, it is compact, yet powerful. You
can write short and to the point expressions
that do a lot.”
- Anders Ivner, foreword to the 2nd edition of The Object Constraint Language
36
71. Some Shortcomings of OCL
Expressions
“... Second, it is compact, yet powerful. You
can write short and to the point expressions
that do a lot.”
- Anders Ivner, foreword to the 2nd edition of The Object Constraint Language
• true for short, straight-forward expressions
• not so true for complex ones
• hard for new users when they move from tutorial
like expressions into real word ones
36
72. The problem with OCL expressions
• OCL in form of expressions that can be chained together
parent.types
->select(name <> '')
->forAll(e | e <> self implies e.name <> self.name)
37
73. The problem with OCL expressions
• The linearity of the expression chaining often leads to long
and complex expressions
self.allContents
->forAll (p | (p.oclIsKindOf(ClassifierRole) implies
p.name = '' implies self.allContents
->forAll (q | q.oclIsKindOf(ClassifierRole) implies
(p.oclAsType(ClassifierRole).base =
q.oclAsType(ClassifierRole).base imples p = q) ) )
and
(p.oclIsKindOf(AssociationRole) implies p.name = '' imples
self.allContents
->forAll ( q | q.oclIsKindOf(AssociationRole) implies
(p.oclAsType(AssociationRole).base =
q.oclAsType(AssociationRole).base imples
p = q) ) )
38
74. The problem with OCL expressions
• The linearity of the expression chaining often leads to long
and complex expressions
self.allContents
->forAll (p | (p.oclIsKindOf(ClassifierRole) implies
p.name = '' implies self.allContents
->forAll (q | q.oclIsKindOf(ClassifierRole) implies
(p.oclAsType(ClassifierRole).base =
q.oclAsType(ClassifierRole).base imples p = q) ) )
and
(p.oclIsKindOf(AssociationRole) implies p.name = '' imples
self.allContents
->forAll ( q | q.oclIsKindOf(AssociationRole) implies
(p.oclAsType(AssociationRole).base =
q.oclAsType(AssociationRole).base imples
p = q) ) )
• This combined with poor tool support
39
Notas del editor
\n
By design, it can only express a limited range of structural constraints, mainly with respect to containment and type conformance. \n\nFor more complex structural constraints usually the Object Constraint Language (OCL) is used that expresses these constraints as state invariants attached to meta-model classifiers\n\n- structural constrains in form of state invariants\n- per/post condition\n
By design, it can only express a limited range of structural constraints, mainly with respect to containment and type conformance. \n\nFor more complex structural constraints usually the Object Constraint Language (OCL) is used that expresses these constraints as state invariants attached to meta-model classifiers\n\n- structural constrains in form of state invariants\n- per/post condition\n
By design, it can only express a limited range of structural constraints, mainly with respect to containment and type conformance. \n\nFor more complex structural constraints usually the Object Constraint Language (OCL) is used that expresses these constraints as state invariants attached to meta-model classifiers\n\n- structural constrains in form of state invariants\n- per/post condition\n
By design, it can only express a limited range of structural constraints, mainly with respect to containment and type conformance. \n\nFor more complex structural constraints usually the Object Constraint Language (OCL) is used that expresses these constraints as state invariants attached to meta-model classifiers\n\n- structural constrains in form of state invariants\n- per/post condition\n
By design, it can only express a limited range of structural constraints, mainly with respect to containment and type conformance. \n\nFor more complex structural constraints usually the Object Constraint Language (OCL) is used that expresses these constraints as state invariants attached to meta-model classifiers\n\n- structural constrains in form of state invariants\n- per/post condition\n
- Some shortcomings we came across while using OCL in an EMF based MDE toolchain -> Motivation\n\n
- Concerned by practical side of OCL rather than formal one\n\n- Most of the issues are well known -> no direct applicable solution to Eclipse EMF\n\n\n
- propagation of runtime exceptions, NPE - difficult to trace\n- difficult to narrow bugs in incorrect OCL expressions\n- OCL console can help, problem with complex expressions that invole derive features, ...\n- Despite the fact that many of these issues have already been identified or addressed, the lack of an overall integration is a crucial issue, which, according to us, influences the slow adoption of OCL in the industry.\n
- complex expressions that goes beyond simple object navigation\n
- with the lack of first-order logic collection operation the expressed concern would be quickly lost among java commands\n- result far from clean\n\n- the no really any highlight\n- emf codegen translates the model concepts into java concepts\n- optional parenthesis, lambda expressions, collection operation, ... \n
- with the lack of first-order logic collection operation the expressed concern would be quickly lost among java commands\n- result far from clean\n\n- the no really any highlight\n- emf codegen translates the model concepts into java concepts\n- optional parenthesis, lambda expressions, collection operation, ... \n
- with the lack of first-order logic collection operation the expressed concern would be quickly lost among java commands\n- result far from clean\n\n- the no really any highlight\n- emf codegen translates the model concepts into java concepts\n- optional parenthesis, lambda expressions, collection operation, ... \n
- with the lack of first-order logic collection operation the expressed concern would be quickly lost among java commands\n- result far from clean\n\n- the no really any highlight\n- emf codegen translates the model concepts into java concepts\n- optional parenthesis, lambda expressions, collection operation, ... \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
- there is also a warning\n
\n
\n
\n
- besides the fact that we can use both java and scala libraries \n - it is a matter of adding a new dependency to the project\n\n- we could simply define a function that takes two booleans\n- the real power is that it will feel like a language feature\n\n- b will not get evaluated => advanced example\n
- besides the fact that we can use both java and scala libraries \n - it is a matter of adding a new dependency to the project\n\n- we could simply define a function that takes two booleans\n- the real power is that it will feel like a language feature\n\n- b will not get evaluated => advanced example\n
- besides the fact that we can use both java and scala libraries \n - it is a matter of adding a new dependency to the project\n\n- we could simply define a function that takes two booleans\n- the real power is that it will feel like a language feature\n\n- b will not get evaluated => advanced example\n
\n
\n
\n
\n
\n
\n
the different semantics between OCL and Scala, \nunlimited numerals, \n\nFinally, carrying out more case studies should help us further assess the practicality of the proposed solution and explore what other advantages of a DSL approach, beyond the traditional usage of OCL, may benefit users.\n
\n
Soon to be fully open sourced\n
1. promising, but we believe that our approach is more pragmatic at least until all the extensions and production ready tools are implemented\n\n2. itself positioned as not a rival to OMG standards but as a prototype to experiment with novel approaches\n\n3. simply snippets of JS - our case represents model extensions as functions - first class citizens at code level\n\n4. external xtext based DSL, does not support constraints, prototype\n\n5. Do modern programming languages make OCL redundant?\n - we try to go beyond OCL-like expressions, advanced constructs, improved invariant support\n
\n
\n
External DSL can choose syntax freely\n Internal DSL has to operate in boundaries of its host language\n Multiple model packages at the same time - classes from different packages\n
External DSL can choose syntax freely\n Internal DSL has to operate in boundaries of its host language\n Multiple model packages at the same time - classes from different packages\n
External DSL can choose syntax freely\n Internal DSL has to operate in boundaries of its host language\n Multiple model packages at the same time - classes from different packages\n
- OCL extension is tool specific\n- selective immutability\n
- it is not criticism of Eclipse OCL\n- files over 800 lines (MacBook Pro, 8GB RAM)\n\n- propagation of runtime exceptions, NPE - difficult to trace\n- difficult to narrow bugs in incorrect OCL expressions\n- OCL console can help, problem with complex expressions that invole derive features, ...\n\n- OCL is either interpreted or translated to some other language\n- Xtext includes Java&#x2019;s debugging support for other languages JSR-045 \n
\n
...that are difficult to understand and maintain.\n\nA constraint related to the collaboration element defined in the UML meta-model adopted from Correa et al\n\n\n
...that are difficult to understand and maintain.\n\nA constraint related to the collaboration element defined in the UML meta-model adopted from Correa et al\n\n\n
...that are difficult to understand and maintain.\n\nA constraint related to the collaboration element defined in the UML meta-model adopted from Correa et al\n\nlack of debugging support\n\n