• Keine Ergebnisse gefunden

Mixing Uni- and Bidirectional Model Transformation

5.4 Applying Object-Tree Lenses to Model Transformation

5.4.4 Mixing Uni- and Bidirectional Model Transformation

Because both the unidirectional MTL which we presented in Chap. 4 and the bidirectional MTL we presented in this chapter are implemented as internal Scala DSLs, they can, in general, be mixed.

5.4. Applying Object-Tree Lenses to Model Transformation 149 Listing 5.27: Constructing a Families2Persons lens in our bidirectional Scala MTL

1 // constructing a type-safe families2persons lens:

2 val adultName = wrap( Focus(_0, $[Member]) ) as[Member,String]

3

4 val childNames = wrap( ListMap(Split(_1, $[Member]) &: Reverse) &: Factorize &:

5 Focus( _1, Term(Term(NullRef::NullRef::NullRef:: NullRef::HNil)::List("")::HNil) )

6 as[List[Member],List[String]]

7

8 val distribute1 = Split(_1, $[Family]) &: TupleDistribute

9

10 val distribute2 = WMap(Id[String] :: Id[String] :: Distribute[String,String]) ::

11 Distribute[String,String]) :: LLNil)

12

13 val strrev = Reverse[String::String::TNil]

14

15 val reverse = WMap(strrev :: strrev :: ListMap(strrev) :: ListMap(strrev) :: LLNil)

16

17 val addCtors = WMap(AddCtor($[Male])) :: AddCtor($[Female]) :: ListMap(AddCtor($[Male))

18 :: ListMap(AddCtor($[Female)) :: LLNil) &: Split(_2)

19

20 val sort = Map( SupertypeListConcat($[Person],$[Male],$[Female]) )

21

22 val extractNames = WMap( Id[String] :: adultName :: adultName :: childNames ::

23 childNames :: LLNil )

24

25 val rearrange = extractNames &: distribute1 &: distribute2 &: addCtors &: sort

26

27 val families2persons = wrap(rearrange) as[Family,Persons]

There are two ways to mix uni- and bidirectional transformation description. A unidi-rectional transformation can be created from a bidiunidi-rectional transformation, or a bidirec-tional transformation can be created from two unidirecbidirec-tional transformations. The first direction is less interesting but easy to accomplish. The forward transformation compo-nentget of any lens which translates between constructor terms can be used as a rule in a unidirectional transformation. Because a rule in our ATL-like MTL mainly consists of an anonymously defined function, we can as well create a rule by directly passing theget function of a lens to the rule’s perform method as shown in line 3 of the following listing.

Listing 5.28: Creating a unidirectional transformation rule from a lens

1 import families2persons.FamiliesMM._, PersonsMM._

2 val f2pLens = wrap(rearrange) as[FamilyCC,PersonsCC] //from previous f2p-lens example

3 val f2pRule = new Rule[FamilyCC, PersonsCC] perform(f2pLens.get)

For the unidirectional transformation language and the bidirectional transformation language to be interoperable, the implicit conversions between domain objects and term objects need to be defined on the case class types we already generate for our unidi-rectional transformations. Thus, domain objects are first converted to their case class counterparts and these case class objects are then converted to corresponding construc-tor term objects. In fact, this makes the to-term conversions easier because the to-case-class conversions already implement the flattening of inheritance hierarchies, which is also needed for term objects. The only thing we need to provide for the above

list-150 Chapter 5. A Compositional Language for Bidirectional Model Transformation ing to work is a generic implicit conversion from Function1[CtorTerm[C,TListC], CtorTerm[A,TListA]] (the general type of the get function of a lens which translates between constructor terms) toFunction1[C,A] by using available implicit conversions.

The other direction, creating a lens from two unidirectional transformations, is more interesting. However, because the backward transformation put of a lens is incremental, we have to extend our unidirectional MTL with discrete incremental transformation ex-ecution. In fact, the standard rule definition syntax in our unidirectional MTL is already suited for incremental transformation. The anonymously defined function takes both the source and the target model element as arguments and returns nothing – it is actually a procedure. When executing the transformation, the transformation engine creates an empty target model element using an EMF factory. This element is then passed as the second argument to the rule procedure which realizes the actual transformation logic by mutating the state of the provided target element. In an incremental transformation exe-cution, the target model element is simply taken from the existing target model which is to be updated incrementally. Thus, we only have to provide such incremental rule execu-tion as an alternativetransformIncrmethod of our unidirectional transformation class.

For providing the possibility to mix unidirectional and bidirectional transformation definition, we then provide a lens helper method which allows an asymmetric lens to be created directly from its two components: a unary, non-incremental forward function and a binary, incremental backward function. Of course, a lens created this way possibly violates the lens laws, so it is not guaranteed that it is a well-behaved bidirectional transformation. The helper function therefore returns a special subtype of lens called UncheckedLens. Using this function, an unchecked lens can be created either from two transformations or from two rules because both can be represented as suitable functions.

In Listing 5.29, we first show the signature of the helper lens creation function and then create a forward and a backward transformation which are, for simplicity, realized by just one transformation rule each. Note that to define the forward transformation’s rule in line 7, we use the alternative rule definition syntax with a unary function which itself creates a target model element, which is then merged with the target element created by the transformation engine. This way, the forward rule can be implicitly converted to a unary function, whereas the backward rule, which we define with the standard two-parameter syntax (line 11), can be implicitly converted to a binary function. The state-mutating procedure is converted to a side-effect free function for this. Thus, because of the availability of these conversions, an unchecked lens can be created by passing those two rules to the lens creation helper method (line 15).

Alternatively, a lens can be created from the non-incremental transformation execution method transform of the forward transformation and the incremental transformation execution method transformIncr of the backward transformation as shown in line 18.

This only works because these transformation execution methods – similarly to our lens implementation – take the root element of a model’s containment hierarchy as input and return the result of the transformation as the target model’s root element.

Although creating lenses from two unidirectional transformations causes the mainte-nance and verification issues which bidirectional transformations are meant to solve (and

5.5. Related Work and Conclusions 151