Skip to main content
Jump to: navigation, search

Difference between revisions of "RoadmapOAW5"

(= Successor to Xpand/Xtend (Work in progress - feedback is highly appreciated))
(Redirecting to OAW)
 
(27 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{| border=0
+
#REDIRECT [[OAW]]
| width=90% |
+
== Successor to Xpand/Xtend (Work in progress - feedback is highly appreciated) ==
+
 
+
It's time to think about a possible successor to Xpand/Xtend. Although the languages have proven well compared to alternatives there are a number of things which can be better and clearer based on our two years of experience with Xpand and Xtend.
+
The main improvements we want to incorporate are:
+
 
+
=== 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, so that
+
imported stuff (types and functions) will be reexported.
+
 
+
Example:
+
 
+
myext1.ext
+
 
+
  foo() : 'foo';
+
 
+
myext2.ext
+
 
+
  reexported import myext1;
+
  bar() : 'bar';
+
 
+
client.ext
+
 
+
  import myext2;
+
  fooBar() : foo()+bar();
+
 
+
=== Generics ===
+
We need full-fledged generics, which can conceptually be copied from Java's generics.
+
 
+
(complicated) Example:
+
  List<M> sort<T extends Comparable<T>, M>(List<M> toSort, (M)=>T closure)
+
 
+
which can be used like this
+
  ['aa','b'].sort(e|e.size); 
+
 
+
You don't have to deal with this complexity if you don't want to define functions which uses generics ;-)
+
 
+
=== 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:
+
 
+
  {
+
    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);
+
  }
+
 
+
  // type of e is inferred from the declaration of the 'select()' function, you don't have to use the curly brackets.
+
  myList.select(e|e.name == "test")
+
 
+
 
+
 
+
==== Type signatures of functions ====
+
The syntax for of a function's type signature looks as follows:
+
 
+
  (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) {
+
    ...
+
  }
+
 
+
=== Functions ===
+
 
+
Functions can be invoked either using the functional syntax or using the member syntax (operation like, aka extensions):
+
 
+
  myFunction(foo, bar) == foo.myFuntion(bar)
+
 
+
A function is declared as follows:
+
 
+
  (private|cached|cached) ReturnType? functionName(declaredParameterList) ( guardExpression )? : bodyExpression ;
+
 
+
Example:
+
  private doStuff(Entity this) name!=null :
+
      name+"Stuff";
+
 
+
The detailed semantics of how the polymorphic resolution works (what role guards play here) and is described in the upcoming section.
+
 
+
or
+
  (private|cached) functionName(declaredParameterList) guardExpression blockExpression
+
 
+
Example:
+
  cached makeAbstract(Entity this) {
+
      abstract := true;
+
      name := 'Abstract'+name;
+
      this;
+
  }
+
 
+
Block expressions are explained in their own section.
+
 
+
==== Polymorphic Resolution with signatures and guards ====
+
Usually polymorphism is based on the types of parameters. The same applies for Xtend++.
+
In contrast to e.g. Java we use the dynamic types (actual types at runtime) of a given set of parameters in order to find the function which
+
best fits (has the most specific declared paramter types).
+
 
+
Example:
+
given the following two functions
+
 
+
  foo(String x) : "string";
+
  foo(Object o) : "object";
+
 
+
this assertions can be made:
+
 
+
  foo('S') == "string"
+
  foo(34)  == "object"
+
  foo((Object)'S') == "string" // would be "object" in Java
+
+
In addition to these concept, commonly known as "multiple dispatch" or "multi method", we introduce guards which can be used to controll the resolution based on the state of a given object not only the type.
+
 
+
Example:
+
 
+
  foo(String x) x.length>5 : "long";
+
  foo(String x) : "short";
+
+
this assertions can be made:
+
 
+
  foo('honolulu') == 'long'
+
  foo("bar") == 'short'
+
 
+
The semantics are as follows:
+
 
+
* First select all possible features (functions and operations), based on the name and the given parameter types.
+
* Then order those features by parameter types (best match first).Those functions with the same parameter type should be order so that the ones having a guard defined come first.
+
 
+
pseudo code
+
 
+
  for (Feature f : features)  {
+
      if (f.hasGuard()) {
+
        if (f.guard.evaluate())
+
            return f; // return the feature where the guard evaluates to true
+
      } else {
+
        return f; // return the feature without a guard
+
      }
+
  }
+
  return null; // no invocation
+
 
+
* if there are features, but the guards evaluate to false, return null:
+
 
+
The static semantics are straight forward:
+
* The guard must be of type boolean.
+
 
+
====Extensions overwrite semantics====
+
Functions and Operations can be overwritten.
+
The precedence is based on th order of imports.
+
Functions from later declared imports overwrite functions introduced before.
+
Local functions overwrite imported functions.
+
Consider overwriting the toString() Operation  (which is invoked on String concatenations) for arbitrary meta types.
+
This will allow very readable templates.
+
 
+
==== dynamically scoped extension overwriting ====
+
Another thing we want to address is the way one can extend generators provided by third parties (like the one shipped with GMF).
+
So far everybody used AOP to "weave" customization and implementation in. The problem is that the generator designer does not really develop for extensibility and every Join Point becomes public API.
+
We've been thinking about  a concept called "dynamic extensions" which is a way to register extensions for a specific call graph.
+
 
+
Example:
+
 
+
  with(&toString(Entity)) {
+
    callGMFCartridge(myEntity);
+
  }
+
 
+
Just to explain: '&toString(Entity) is a literal pointing to the 'toString(Entity)'-function. So one could write '&toString(Entity).evaluate(myEntity)' instead of 'myEntity.toString()' for example. The implementation of the function will be used whenever such a function is invoked within the callGMFCartridge(Entity) function (the third party cartridge).
+
In other words  one overwrites the toString() function for Entities for the following block.
+
So what you as a generator developer could do is, provide a list of function which can be overwritten. In addition there is a final keyword, which prevents overwriting the corresponding function.
+
 
+
This won't be a replacement for AOP, but we think that the AOP feature has been missused in order to provide extensibility.
+
In addition the extension emchanism was bound to the static context so far which really is a limitation in some situations.
+
 
+
=== 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)
+
 
+
It's something like a pseudo imperative syntax (but still is an expression!).
+
 
+
Variables are assign-once!
+
 
+
Example:
+
 
+
  myExtension(String stuff) {
+
    def x := stuff.length();
+
    if x>56 then
+
        "Foo"
+
    else
+
        "Bar";
+
    endif
+
  }
+
 
+
A code block is itself an expression consisting of a list of expressions.
+
It returns the value returned by the last expression.
+
 
+
It is possible to overwrite the scope.
+
Example:
+
 
+
  doStuff() {
+
    def x := "Foo";
+
    {
+
      def x:= "Bar";
+
      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.
+
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.
+
 
+
The scope of the caching is per execution context, which can be reused in several invocations?
+
+
Examples:
+
 
+
  var paramPerOperationAndName := new Parameter cachedwith op,name {
+
      this.name := name;
+
      type := aDatatype;
+
  }
+
 
+
  var localSingleton := new Foo cachedwith {
+
      stuff := "bla";
+
  }
+
 
+
==== 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 Entity as localRef {
+
      // 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
+
        }
+
      }
+
 
+
=== Operator Overloading ===
+
There will be predefined operators which can be used instead of the usual function invocation syntax if there is an operator for a name and a specific number of parameters.
+
 
+
Some examples:
+
 
+
  add(Object a, Object b) => a + b
+
  subtract(Object a, Object b) => a- b
+
  not(Object a) => !a
+
 
+
The && and || operators are not overwriteable because of there special semantics wrt lazy evaluation.
+
 
+
=== Overloading accessors ===
+
If a function's signature follows the pattern
+
  String getFoo()
+
 
+
it can be invoked using property syntax:
+
  this.foo
+
 
+
If a function's signature follows the pattern
+
  void setFoo(String)
+
 
+
it overwrites the modify of the 'foo' property, hence it will be used within assignments:
+
  foo := 'Holla'
+
 
+
It is also possible to use such functions without having a corresponding property like:
+
 
+
  getJavaName(Entity this) {
+
      if abstract then
+
        'Abstract'name
+
      else
+
        name
+
      endif
+
  }
+
 
+
which can be used like:
+
 
+
myTemplate(Entity this) :»
+
  public class «javaName» {
+
  }
+
«;
+
 
+
=== Templates ===
+
A template is essentially a function returning a String. I always disliked that it is not possible to mix functions and templates within one file. I also find the invocation of templates (EXPAND bla FOR foo) too verbose.
+
 
+
Example:
+
  myTemplate() :»
+
    package «packageName»;
+
    public class «name» {
+
      «foreach (attributes as a)»
+
          «if (a.type!=null) then»
+
            public «a.type» «a.name»;
+
          «endif»
+
      «endforeach»
+
    }
+
  «;
+
 
+
It's just a string literal with the xpand syntax within. Instead of '«' and '»' one can also use the common literal syntax 'foo' and "bar".
+
 
+
The FILE statement will be removed.
+
Files can be opened through extensions:
+
 
+
  generateCode(Entity e) :
+
    writeToFile(e.JavaFile(),e.toJava());
+
 
+
==== XString - mutable, streamable, lazy string ====
+
We want to come up with a special datatype called XString, which is mutable, streamable and evaluated lazy (on invocation of toString()).
+
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()»;
+
      «(def imports:=»
+
        import java.util.*;
+
      «)»     
+
      public class  ...     
+
          »assertImported(imports,"java.math.*")«
+
      }
+
  «;
+
 
+
  assertImported(XString this, String import) :
+
      if !this.contains(import) then
+
          this.append("import "import";");
+
  ;
+
 
+
 
+
 
+
=== 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)? endif
+
 
+
The else part is optional and will return null if not defined.
+
 
+
Example:
+
  // The following expression will return null:
+
  if (false) "Holla" endif
+
 
+
'endif' is optional.
+
 
+
 
+
 
+
 
+
 
+
=== BK added --- To be discussed ===
+
==== AOP for Checks ====
+
When modifying an expression using AOP, there might be the situation that a check is no longer valid. It has to be modified as well
+
 
+
SE: We can't modify expressions using AOP. We just can wave an Advice around a function call.
+
Shouldn't the designer of a cartridge have this in mind when declaring Join Points to be adviced by clients?
+
I think if an advice breaks an existing constraint it breaks the contract of the adviced function.
+
 
+
=== PF added --- to be discussed ===
+
==== Add a FOLDER keyword ====
+
In order to create empty directory structures, it would be nice to have a FOLDER keyword.
+
Syntax:
+
«FOLDER expression»
+
 
+
SE: I think such things should be implemented using library functions.
+

Latest revision as of 08:59, 9 December 2008

Redirect to:

Back to the top