|
|
(66 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
− | {| border=0
| + | #REDIRECT [[OAW]] |
− | | width=90% |
| + | |
− | == Collection of features (rough) ==
| + | |
− | | + | |
− | In Version 5 we want to improve some of the Xtend language concepts and features.
| + | |
− | Codename is Xtend++ :
| + | |
− | | + | |
− | === Imports ===
| + | |
− | The import mechanism should be reworked, so that every import is explicit.
| + | |
− | We won't need any metamodel configuration in the workflow nor in the editors anymore.
| + | |
− | This will not only make the setup simpler but will also improve the performance.
| + | |
− | | + | |
− | The syntax would change to something like the following:
| + | |
− | | + | |
− | import org:openarchitectureware:Extension; // native import
| + | |
− | import EMF "my/package/model.ecore"; // non-native import
| + | |
− | import UML "my/test.profile.uml" as profile; // non-native import with name space definition
| + | |
− | import Java "my.test.Class"; // non-native import introduces java types and features form my.test.Class
| + | |
− | import XSD "http://www.mywebsite.org/xsd/metamodel.xsd" // non-native import importing the types from an XSD file
| + | |
− | ... (think of Hibernate mapping files, Groovy, etc.)
| + | |
− | | + | |
− | ==== Native import ====
| + | |
− | A native import refers to another extension file imports all public members (types, functions, extensions).
| + | |
− | | + | |
− | ==== Non-native Import ====
| + | |
− | A non native import starts with an identifier pointing to an installed adapter.
| + | |
− | The adapter is responsible for loading and converting the type information from the given string. The syntax in the string is defined by the adapter. The token directly after the
| + | |
− | import keyword defines which adapter to use.
| + | |
− | | + | |
− | ==== Namespace definition ====
| + | |
− | All members are included without namespace information. If you need a namespace you can explicitely define one per import.
| + | |
− | | + | |
− | ==== Reexport ====
| + | |
− | The reexport keyword will be supported.
| + | |
− | | + | |
− | MV: What will it do?
| + | |
− | <br>BK: Reexport an import to "downstream" extensions
| + | |
− | | + | |
− | === Generics ===
| + | |
− | We need full-fledged generics, which can conceptually be copied from Java's generics.
| + | |
− | Maybe we can leave out some of the more advanced capabilities?
| + | |
− | | + | |
− | === Closures ===
| + | |
− | We'll have real closures, not the built-in stuff we have now.
| + | |
− | Closure syntax:
| + | |
− | | + | |
− | parameterList '|' expression-using-parameters-and-scope
| + | |
− | | + | |
− | Where parameter list must be typed, either implicitly or explicitly.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | // type of e is inferred from the declaration of the 'select()' function
| + | |
− | myList.select(e|e.name == "test")
| + | |
− | | + | |
− | or
| + | |
− | {
| + | |
− | String myText := "test";
| + | |
− | | + | |
− | (Attribute)=>Boolean myClosure := e|e.name==myText; // e is inferred from the declared type of the assignee
| + | |
− | | + | |
− | myList.select(myClosure);
| + | |
− | }
| + | |
− | | + | |
− | alternatively declare the parameter types explicitly
| + | |
− | {
| + | |
− | var myText := "test";
| + | |
− | var myClosure := Attribute e|e.name==myText;
| + | |
− | myList.select(myClosure);
| + | |
− | }
| + | |
− | | + | |
− | | + | |
− | ==== Declaring Closure types ====
| + | |
− | The syntax for of a closure type is
| + | |
− | | + | |
− | MV: actually what you do here is not declare closure types, but rather function
| + | |
− | signatures. Closures are more or less function literals. So there are several things:
| + | |
− | * function signatures
| + | |
− | * anonymous functions (closures)
| + | |
− | * closures assigned to a variable (which makes them named functions defined via a closure)
| + | |
− | * and normal functions
| + | |
− | stuff like select(...) does not expect a closure, rather it is typed with a function
| + | |
− | literal (something like (Attribute)=>boolean). You can pass either
| + | |
− | * a literal closure
| + | |
− | * a refernce to a named closure
| + | |
− | * or a reference to a function
| + | |
− | | + | |
− | (parameterTypes)=>returnType
| + | |
− | | + | |
− | Examples:
| + | |
− | | + | |
− | ()=>Object
| + | |
− | (String)=>Boolean
| + | |
− | (String, Entity)=>Entity
| + | |
− | | + | |
− | Example 2: declaration of higher-order functions using generics :
| + | |
− | | + | |
− | List<T> select<T>(List<T> this, (T)=>Boolean closure) {
| + | |
− | ...
| + | |
− | }
| + | |
− | | + | |
− | === Extensions/Functions ===
| + | |
− | In the first version there will only be functions (aka. extensions).
| + | |
− | Functions can be invoked either using the functional syntax or using the member syntax (operation like):
| + | |
− | | + | |
− | myFunction(foo, bar) == foo.myFuntion(bar)
| + | |
− | | + | |
− | A function is declared as follows:
| + | |
− | | + | |
− | (private|cached) ReturnType? functionName(declaredParameterList) guardExpression? : bodyExpression ;
| + | |
− | | + | |
− | Example:
| + | |
− | private doStuff(Entity this) name!=null :
| + | |
− | name+"Stuff";
| + | |
− | | + | |
− | MV: What happens if the guard is false? Not executed? Another one of the same name is executed?
| + | |
− | <br>> BK: See next section. I'd prefer to have one expression with NO guard which is used as default if all the others evaluate to false. I think everything else will be the hell for the tooling!
| + | |
− | | + | |
− | or
| + | |
− | | + | |
− | (private|cached) functionName(declaredParameterList) guardExpression blockExpression
| + | |
− | | + | |
− | MV: What is a block expression?
| + | |
− | <br>> BK: just an expression
| + | |
− | | + | |
− | Example:
| + | |
− | cached makeAbstract(Entity this) {
| + | |
− | abstract := true;
| + | |
− | name := 'Abstract'+name;
| + | |
− | return this;
| + | |
− | }
| + | |
− | | + | |
− | ==== Polymorphic Guards ====
| + | |
− | The guards are used for polymorhpic resolution of a called extension.+
| + | |
− | | + | |
− | MV: Polymorphic is maybe the wrong term, because it is associated with subtyping.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | context Entity {
| + | |
− | String javaName() isAbstract : "Abstract"+name;
| + | |
− | String javaName() : name;
| + | |
− | }
| + | |
− |
| + | |
− | | + | |
− | | + | |
− | The dynamic semantics are:
| + | |
− | | + | |
− | * First select all possible features (functions and operations), based on the name and the types.
| + | |
− | * Then order those features by parameter types (best match first).
| + | |
− | * For each feature
| + | |
− | * evaluate the guard
| + | |
− | * if it doesn't evaluate to true skip it
| + | |
− | * if it evaluates to true
| + | |
− | * make sure that there is no second feature with the same signature where the guard evaluates to true
| + | |
− | * if there is no such feature, we've found our feature -> success!
| + | |
− | * else throw new Exception("Unambigous feature ... more than one feature ... bla")
| + | |
− | | + | |
− | * if there are features, but the guards evaluate to false, return null:
| + | |
− | | + | |
− | MV: Why null? Why not fail?
| + | |
− | <br>> BK: see my comment above
| + | |
− | | + | |
− | * if there is no feature, do "feature not found" (i.e. raising exception or invoking 'featureNotFound' operation)
| + | |
− | | + | |
− | The static semantics are straight forward: The guard must be of type boolean.
| + | |
− | | + | |
− | MV: do we want to do static verification that all cases are covered?
| + | |
− | <br>> BK: see my comment above
| + | |
− | | + | |
− | ====Extensions overwrite semantics====
| + | |
− | Extensions with the same signature will overwrite Operations.
| + | |
− | Consider overwriting the toString() Operation (which is invoked on String concatenations) for arbitrary meta types.
| + | |
− | This will allow very readable templates.
| + | |
− | | + | |
− | === Code blocks ===
| + | |
− | | + | |
− | A code block is the replacement for chain expressions ( a-> b-> x) with the additional features:
| + | |
− | * provides variable declarations (Expression returning the assigned value)
| + | |
− | * support early exit using the 'return' keyword, which forces outer code blocks to exit themselfes.
| + | |
− | | + | |
− | It's something like a pseudo imperative syntax (but still is an expression!).
| + | |
− | | + | |
− | MV: Variables are assign-once, right?
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | myExtension(String stuff) {
| + | |
− | var x := stuff.length();
| + | |
− | if (x>56)
| + | |
− | return "Foo";
| + | |
− | else {
| + | |
− | return "Bar";
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | A code block is itself an expression consisting of a list of expressions.
| + | |
− | It returns the value returned by the first executed "return-expression", or the value of the last expression.
| + | |
− | | + | |
− | It is possible to overwrite the scope.
| + | |
− | Example:
| + | |
− | | + | |
− | doStuff() {
| + | |
− | var x := "Foo";
| + | |
− | {
| + | |
− | var x:= "Bar";
| + | |
− | return x;
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | will return "Bar"
| + | |
− | | + | |
− | === Object creation expression ===
| + | |
− | We are thinking about a syntax to create model graphs inline.
| + | |
− | We need this not only for model transformations but also for writing model transformation tests.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | new Entity {
| + | |
− | name := "Person";
| + | |
− | references += new Reference {
| + | |
− | name := "someRef"
| + | |
− | type := anotherEntity
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | ==== Assignment Expressions ====
| + | |
− | They are just another syntax for invoking a setter resp. adder-operation (which will be removed).
| + | |
− | | + | |
− | MV: Great!
| + | |
− | <br>> BK: I'd prefer not to remove them. (Compatibility) Do we intent to support operator overloading? Then, this could just be a function with two arguments
| + | |
− | | + | |
− | They return the assigned value.
| + | |
− | | + | |
− | ==== create / cache semantics ====
| + | |
− | The creation expression should replace the "create extension" mechanism from Xtend 1.0.
| + | |
− | A creation of an Object is cached if the type name is suffixed with parenthesis containing any number of arguments.
| + | |
− | The arguments act as a key.
| + | |
− | | + | |
− | MV: What is the scope of the caching? One Xtend Component invocation?
| + | |
− | | + | |
− | Examples:
| + | |
− | | + | |
− | var paramPerOperationAndName := new Parameter(op,name) {
| + | |
− | this.name := name;
| + | |
− | type := aDatatype;
| + | |
− | }
| + | |
− | | + | |
− | var localSingleton := new Foo() {
| + | |
− | stuff := "bla";
| + | |
− | }
| + | |
− | | + | |
− | MV: The syntax with the arguments in the params looks quite a bit like
| + | |
− | a constructor. This is confusing. Can we use something else? like using
| + | |
− | < and >, for example?
| + | |
− | | + | |
− | ==== cross referencing ====
| + | |
− | | + | |
− | We need a way to specify cross references within a declared tree. The problem is that we need a reference to a created type after it has been created and before it will be initialized. This can be accomplished by adding a special assignment construct:
| + | |
− | | + | |
− | var x := new localRef:=Entity {
| + | |
− | // x is not visible here, because the right hand expression has not been evaluated so far.
| + | |
− | // localRef holds a reference to the created but not yet initialized entity.
| + | |
− | name := "Person";
| + | |
− | references += new Reference {
| + | |
− | name := "partner"
| + | |
− | type := localRef
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | MV: hm, the syntax with the two := is not very nice. How about:
| + | |
− | | + | |
− | var x := new Entity as localref {
| + | |
− | | + | |
− | | + | |
− | === XString (Template syntax) ===
| + | |
− | We want to come up with a special datatype calle XString, which has a special literal syntax (like Xpand template syntax) and is mutable and streamable.
| + | |
− | | + | |
− | Example:
| + | |
− | toJava(Entity this) :"""
| + | |
− | package «packageName()»;
| + | |
− |
| + | |
− | public class «name» {
| + | |
− | «FOREACH attributes AS a»
| + | |
− | public «a.type» «a.name»;
| + | |
− | «ENDFOREACH»
| + | |
− | }""";
| + | |
− | | + | |
− | It's just a string literal with the xpand syntax within. The terminals '«' and '»' should be configurable (or there should be an alternative at least).
| + | |
− | | + | |
− | The FILE statement will be removed.
| + | |
− | Files can be opened through extensions:
| + | |
− | | + | |
− | generateCode(Entity e) :
| + | |
− | writeToFile(e.JavaFile(),e.toJava());
| + | |
− | | + | |
− | MV: Nice!!
| + | |
− | | + | |
− | ==== Container (NOT CLEAR HOW THIS COULD WORK) ====
| + | |
− | Because XStrings are mutable and are converted to a string late, it is possible to create a tree structure containing XStrings, where
| + | |
− | you can add XStrings (or normal Strings) later on.
| + | |
− | | + | |
− | Example:
| + | |
− | toJava(Entity this) :"""
| + | |
− | package «packageName()»;
| + | |
− | «imports()»
| + | |
− |
| + | |
− | ...
| + | |
− | """;
| + | |
− | | + | |
− | cached imports(Entity this) :"""
| + | |
− | import java.util.*;
| + | |
− | """;
| + | |
− |
| + | |
− | ... to be continued (and cleaned up;-))
| + | |
− | | + | |
− | | + | |
− | === if expression ===
| + | |
− | As seen in the previous example, we want an if-expression. using if, else keywords.
| + | |
− | | + | |
− | if (predicate) expression (else if (predicate) expression)* (else expression)?
| + | |
− | | + | |
− | The else part is optional and will return null if not defined.
| + | |
− | | + | |
− | Example:
| + | |
− | // The following expression will return null:
| + | |
− | if (false) "Holla"
| + | |
− | | + | |
− | ===Declarative constraints===
| + | |
− | The language check will be integrated into Xtend. That is you can define checks and extensions in the same file:
| + | |
− | | + | |
− | allEntities(emf::EObject obj) : eRootContainer.eAllContents.typeSelect(Entity);
| + | |
− |
| + | |
− | context Entity warning "name "+name+" should start with an uppercase letter" :
| + | |
− | name.firstToLowerCase()!=name
| + | |
− | context Entity error "duplicate name "+name :
| + | |
− | allEntities(this).select(e|e.name==name).size==1;
| + | |
− | | + | |
− | of course you can use everything we have defined before in checks, too. Example :
| + | |
− | | + | |
− | context Entity error "duplicate name "+name {
| + | |
− | var entities := eRootContainer.eAllContents.typeSelect(Entity);
| + | |
− | allEntities(this).select(e|e.name==name).size==1;
| + | |
− | }
| + | |
− | | + | |
− | === Definition of Types (later) ===
| + | |
− | So far we couldn't define Types within Xtend but had to define them using other techniques (ecore, Java, UML-profile, etc.).
| + | |
− | Defining tyoes within Xtend would be a great feature. Because it is much simpler and faster to write them in text.
| + | |
− | In addition we could define Type with logic (operations).
| + | |
− | A syntax could look like this:
| + | |
− | | + | |
− | type Entity extends Named {
| + | |
− | // simple attributes
| + | |
− | String name;
| + | |
− | Boolean isAbstract {
| + | |
− | private set(aValue)
| + | |
− | get : name.startsWith("Abstract");
| + | |
− | };
| + | |
− |
| + | |
− | // references
| + | |
− | Set<Entity> superTypes;
| + | |
− | Set<Features>* features; // asterisk means containment
| + | |
− | Set<Reference>* references subsets features;
| + | |
− |
| + | |
− | // operations
| + | |
− | doStuff(String x) : x+name;
| + | |
− | doMoreStuff(String x) {
| + | |
− | name := x;
| + | |
− | features += var f := new Feature{
| + | |
− | f.name:= x;
| + | |
− | ...
| + | |
− | };
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | MV: I agree that this is not the most urgent feature.
| + | |