• No results found

Domain specific embedded languages and model driven engineering

N/A
N/A
Protected

Academic year: 2021

Share "Domain specific embedded languages and model driven engineering"

Copied!
67
0
0

Bezig met laden.... (Bekijk nu de volledige tekst)

Hele tekst

(1)

UNIVERSITY OF TWENTE.

Domain Specific Embedded Languages and Model Driven Engineering

MSc CS SE Stefan Kroes

SE Group

Graduation Committee:

Dr. Ivan Kurtev Dr. ir. Klaas van den Berg

A. G¨oknil, MSc

February 14, 2010

(2)

Contents

1 Introduction 1

1.1 Problem Statement . . . . 1

1.2 Approach . . . . 2

1.3 Related Work . . . . 2

1.4 Contributions . . . . 3

1.5 Outline . . . . 3

2 Problem Analysis 4 2.1 Host Language Choice and Features (Q1 and Q2) . . . . 5

2.2 MTL Features (Q3) . . . . 5

2.3 Evaluating the degree of reuse (Q4) . . . . 5

2.4 Advantages and Disadvantages (Q5) . . . . 6

2.5 Comparison to other implementations (Q6) . . . . 6

2.6 Conclusions . . . . 6

3 Model Driven Engineering 8 3.1 Conceptual Overview . . . . 8

3.2 Definition of Model . . . . 9

3.3 MDE or MDA? . . . 10

3.4 Promises of MDE . . . 10

3.4.1 Separation of Concerns . . . 10

3.4.2 Better communication . . . 10

3.4.3 Maintainability . . . 11

3.5 Problems of MDE . . . 11

3.5.1 Behavioural Modelling . . . 11

3.5.2 Modelling of Dynamic Languages . . . 11

3.6 Tool Support . . . 11

3.6.1 Query/View/Transformation (QVT) . . . 12

3.6.2 ATLAS Transformation Language (ATL) . . . 12

3.6.3 RubyTL . . . 12

3.6.4 RGen . . . 12

3.7 Features of MTL’s . . . 12

3.7.1 Specification Mechanism . . . 12

3.7.2 Rule, Source and Target Multiplicity . . . 12

3.7.3 Source, Target or Hybrid Models . . . 13

3.7.4 Tracing Facilities . . . 13

3.7.5 Transformation Ordering . . . 13

3.7.6 Source and Target Model Multiplicity . . . 14

3.7.7 Source Expression Language . . . 14

3.7.8 Helpers Functions . . . 15

3.8 Conclusions . . . 15

(3)

4 Ruby and Domain Specific Embedded Languages 16

4.1 Historical Context . . . 16

4.2 Domain Specific Languages . . . 17

4.3 The Ruby Syntax . . . 17

4.4 Object Oriented Programming Features . . . 18

4.4.1 Open Imperative Class Definitions . . . 18

4.4.2 Method Chaining . . . 20

4.4.3 Duck Typing . . . 21

4.4.4 Operator Overloading . . . 21

4.5 Prototype Oriented Programming Features . . . 22

4.6 Functional Programming Features . . . 22

4.6.1 Ruby Blocks and Domain Specific Embedded Language (DSEL)’s . . . 23

4.7 Aspect Oriented Programming Features . . . 23

4.7.1 Catching missing constants to clean up a DSEL . . . 24

4.8 Conclusions . . . 24

5 TRans: A MTEL prototype 25 5.1 A Basic Modelling Language . . . 25

5.1.1 Simple Model Definition DSEL . . . 27

5.1.2 Loading and Saving Models . . . 27

5.1.3 Querying Models using OCL . . . 27

5.2 TRans Introduction . . . 29

5.2.1 A Complete Transformation . . . 29

5.2.2 Transformation Loading and Execution . . . 29

5.2.3 Model Declarations . . . 30

5.2.4 Requiring Libraries . . . 31

5.2.5 Transformation Rules . . . 31

5.2.6 Helpers . . . 32

5.3 TRans AOP Features . . . 32

5.4 Tracing in TRans . . . 34

5.4.1 Tracing Constraints . . . 34

5.4.2 Return Types . . . 35

5.4.3 Result Count Conditions . . . 35

5.5 Code Generation . . . 35

5.6 Conclusions . . . 36

6 TRans: Architecture, Design and Implementation 38 6.1 Architecture . . . 38

6.2 Design . . . 39

6.2.1 DSEL Tools Component . . . 39

6.2.2 AOP Tools Component . . . 41

6.2.3 Transformation Representation Component . . . 42

6.2.4 Transformation Execution Component . . . 43

6.2.5 Model Component . . . 44

6.3 Implementation . . . 45

6.4 Conclusions . . . 46

7 Evaluation 47 7.1 Comparison to Classic Language Design Approach . . . 47

7.1.1 Achieved Reuse . . . 47

7.1.2 Advantages . . . 47

7.1.3 Disadvantages . . . 48

7.1.4 Comparison Table . . . 49

7.2 Comparison to RubyTL . . . 49

7.2.1 Advantages . . . 49

7.2.2 Disadvantages . . . 50

(4)

7.2.3 Comparison Table . . . 51

7.3 Conclusions . . . 51

8 Conclusions 52 8.1 Summary of Concepts . . . 52

8.2 Research Questions . . . 53

8.2.1 Research Question 1 . . . 53

8.2.2 Research Question 2 . . . 53

8.2.3 Research Question 3 . . . 53

8.2.4 Research Question 4 . . . 54

8.2.5 Research Question 5 . . . 54

8.2.6 Research Question 6 . . . 54

8.3 Contributions . . . 55

8.3.1 Architectural Improvements . . . 55

8.3.2 Comprehensive Approach . . . 55

8.3.3 AOP Functionality for Model Transformation . . . 55

8.3.4 Complete Survey of DSEL Techniques . . . 55

8.4 Future Work . . . 55

8.4.1 Disadvantages . . . 55

(5)

List of Figures

1.1 Reading guide for this report . . . . 3

2.1 How we expect different characteristics of our implementation to be affected by increased reuse . . . . 7

3.1 The relation between a model and its subject . . . . 8

3.2 Models and their meta-models . . . . 9

3.3 An overview of the process of model transformation and its terminology . . . . 9

3.4 Example of keeping a trace information . . . 13

4.1 Example of Ruby syntax . . . 17

4.2 Example of using named arguments . . . 18

4.3 Examples of calling methods on literals . . . 18

4.4 Meta-objects, regular objects and the relations between them . . . 19

4.5 Examples of imperative class definition . . . 19

4.6 Opening the string class . . . 20

4.7 A simple recipes DSEL . . . 20

4.8 A simple DSEL for performing set mathematics . . . 21

4.9 Singleton method definition example . . . 22

4.10 Example of using procs . . . 22

4.11 Using blocks to create a DSEL with implicit context . . . 23

4.12 Demonstration of the XMLBuilder gem . . . 23

4.13 Using the const missing method to clean up the recipe DSEL . . . 24

5.1 Meaning of and relations between different concepts used in this report . . . 26

5.2 Sample code for dealing with models . . . 26

5.3 DSEL for defining models in code . . . 27

5.4 Sample code for loading and saving models . . . 27

5.5 Sample YAML model file with two elements . . . 28

5.6 Sample of our OCL variant, implemented as a DSEL . . . 28

5.7 A complex transformation . . . 29

5.8 Code sample of loading and executing a TRans transformation . . . 30

5.9 Diagram showing the model transformation pattern of TRans . . . 30

5.10 Code sample declaring a source and a target model . . . 31

5.11 Code sample of requiring a Ruby Gem from a TRans transformation . . . 31

5.12 Code sample of a simple rule in TRans . . . 32

5.13 Code sample of a TRans helper . . . 32

5.14 Code sample of a TRans join point logger . . . 33

5.15 Sample output for the code sample in figure 5.14 . . . 33

5.16 Changing the join point logger to use ’ before’ and ’ after’ methods . . . 33

5.17 Sample code for defining a concern from the transformation . . . 34

5.18 An example of a simple trace in TRans . . . 34

5.19 An example of element constraints for our basic modelling language . . . 35

5.20 A code generation sample . . . 36

5.21 A code generation sample . . . 36

(6)

6.1 Architectural logical overview diagram . . . 39

6.2 Class diagram of the DSEL Tools component . . . 40

6.3 DSEL definition code taken from ’TransformationElement’ class . . . 41

6.4 Sample DSEL code for DSEL definition in figure 6.3 . . . 41

6.5 Sequence diagram of handling DSEL keywords . . . 42

6.6 Class diagram of the AOP Tools component . . . 42

6.7 Sequence diagram showing the weaving of concerns for a join point . . . 43

6.8 Class diagram of the Transformation Representation component . . . 43

6.9 Class diagram of the Transformation Execution component . . . 44

6.10 Squence diagram of the Transformation Execution algorithm . . . 45

(7)

List of Tables

5.1 Functional equivalents to OCL in our ruby implementation . . . 28

5.2 Possible values for the ’returns’ option for the trace method . . . 35

7.1 Comparison table for the classic approach to language design versus the DSEL approach 49

7.2 Comparison table for RubyTL versus TRans . . . 51

(8)

Abbreviations

AOP Aspect Oriented Programming API Application Programmer Interface ATL ATLAS Transformation Language DSEL Domain Specific Embedded Language DSL Domain Specific Language

EMF Eclipse Modelling Framework MDA Model Driven Architecture MDE Model Driven Engineering

MTEL Model Transformation Embedded Language MTL Model Transformation Language

OCL Object Constraint Language

OMG Object Management Group

QVT Query/View/Transformation

UML Unified Modelling Language

(9)

Abstract

Model Driven Engineering (MDE) [20] is an emerging approach to software engineering. The main concepts in MDE are models, meta-models and transformations. Models are the first class entities of any MDE environment. A model is a representation of its subject, describing only the relevant aspects of it and omitting everything else. While a model’s subject is represented by the model, the model can be interpreted to obtain information about its subject. The subjects of these models are parts of aspects of the software system under development. Models can be refined and (de)composed using transformations, defined in a Model Transformation Language (MTL).

MTL’s are often implemented using a traditional approach to language design, using a dedicated compiler and/or interpreter. We will explore an alternative but equally valid method of developing a MTL, namely developing it as an Domain Specific Language (DSL) within the context of an existing general purpose programming language (host language). This kind of DSL is often referred to as an DSEL [18] and it’s essentially an advanced form of the Application Programmer Interface (API).

We chose Ruby as our host language because of its concise and flexible syntax, extensive features for the creation of DSEL’s, support for declarative and functional programming, reflective features and mature standard library. For our goal, the most important of these are code blocks that can be evaluated in any context and operator overloading.

This report introduces and explains our Model Transformation Embedded Language (MTEL) called TRans. The defining characteristics of TRans, apart from it being a MTEL, are support multiple source and target models, automatic execution ordering using a topological sort algorithm, advanced tracing functionality and Aspect Oriented Programming (AOP) features. By implementing TRans as a MTEL we where able to reduce implementation effort significantly. We deem that this reduction far outweighs the disadvantages of this approach, especially for research purposes.

The research that is most obviously related to our research is the RubyTL language for model trans-

formation. It too is a MTEL based on Ruby. When compared to RubyTL, TRans has the following

advantages: some AOP support, decoupling from modelling language, a more advanced DSEL and a

cleaner implementation. We where able to achieve these advantages because Ruby DSEL creation tech-

niques, as well as the MDE domain, have evolved and TRans is a more recent effort.

(10)

Chapter 1

Introduction

Most innovative efforts in the field of software engineering attempt to abstract away from the specifics of the actual machine. We strive to create environments in which software engineers can consider functionality as much as possible rather than having to worry about the specifics of their implementation platform. The main consideration for these abstractions is the need to hide as much complexity as possible while limiting possibilities as little as possible. We started off in this direction quite nicely by building two layers of abstraction of top of the actual machine code; assemblers and programming languages.

Programming languages where in turn used develop interpreters and virtual machines, paving the way for multi-platform and dynamic programming languages. While many efforts are being made to improve programming languages themselves, a new layer of abstraction that builds on them has not emerged for a long time. Model Driven Engineering (MDE) [20] is believed by many to be this next layer of abstraction.

MDE is concerned with models and transformations between these models. From a practical point of view models are essentially simple data structures that can be used to express anything. From a theoretical point of view however, the concept of a model is much broader. A model is an abstraction of its subject, describing only the relevant aspects of it and omitting everything else. This means multiple models can be abstractions of the same subject, for example a software system, each model describing different aspects of it. In the ideal model driven software project, models expressing individual concerns are mapped (optionally through intermediate models) to models expressing an executable system, often in the form of programming code. This is an exciting new idea, promising true separation of concerns, better maintainability and more effective communication through different and often graphical representations of models.

Transformations and mappings between models are two terms describing the same thing, chosen from an imperative and declarative point of view respectively. In the rest of this report we will refer to this ac- tivity as model transformation, as it’s so much more common. The process of model transformation takes one or more models as input and produces one or more output models from them. These output models can either be an updated version of an input model or a newly generated model. Model transformations are expressed in a model transformation language and are essential to model driven engineering.

This chapter discusses the current and desired situations in the field of model transformation lan- guages, our approach to solving some of these problems and finally the outline of this thesis.

1.1 Problem Statement

MDE is currently not the prevalent method of implementing software systems, even though the first

examples of MDE can be traced back to the 1980’s. MDE has some serious problems that need to be

solved before it can assume a more significant role in software development. From a theoretical point of

view, a major problem with MDE is the inability to unambiguously and completely describe a system

using only conceptual models. This applies particularly to the modelling of the behavioural aspects of

the system. Additionally, there are practical problems with adoption and tool support. The theoretical

and practical problems enforce each other in a vicious cycle. A lack of adoption causes MDE to evolve

at a very slow pace while at the same time the immaturity of MDE is preventing it from being adopted

(11)

on a large scale.

This study is concerned with the more practical problems of MDE, and model transformations in particular. A language that is designed specifically for this purpose is called a Model Transformation Language (MTL). A handful of relatively mature languages exist in this area, the most notable of which are QVT [27], ATLAS Transformation Language (ATL) [5] [16] and Tefkat [23]. These languages were developed using a classic approach which means defining a grammar and developing a dedicated parser and interpreter or compiler for them. While this is a valid approach that provides maximum flexibility, history has shown that these kinds of languages take a long time to mature. Some major languages from different areas of computer programming that where developed in this fashion are C, Java and Ruby, which all took the better part of a decade to become mature, standardised and generally accepted.

We also observe an overlap in desirable language features between modern programming languages and MTL’s. Aspects like parsing and interpretation, expressions, data structures, garbage collection, I/O and other libraries, method dispatching, control structures and exception handling are all desirable in a MTL and have already been implemented in most major programming languages. Ideally we could borrow these language features from a mature programming language and focus solely on model transformation specific problems when developing a MTL. Borrowing all these facilities from another language wouldn’t only save us the burden of implementing them ourselves but would also result in a better product. Large parts of the MTL would have matured much longer than the lifespan of the MTL itself and would already be familiar to programmers who are familiar with the chosen programming language.

Our problem statement is in a single sentence:

Designing a MTL from scratch is a time consuming endeavour that requires re-implementation of many language features that already exist (mature and thoroughly tested) in other programming languages.

1.2 Approach

We will explore an alternative but equally valid method of developing a MTL, namely developing it as an Domain Specific Language (DSL) within the context of an existing general purpose programming language. This kind of DSL is often referred to as an Domain Specific Embedded Language (DSEL) [18]

and it’s essentially an advanced form of the Application Programmer Interface (API).

In our opinion, the most appropriate way to explore this approach is to perform a case study, which in this case means the implementation of a MTL using this approach. In order to do this we’ll need to pick a general purpose programming language to base our MTL on, which we will call the host language. We will need to establish some criteria for picking our host language and examine its facilities for DSEL creation. Additionally we need to perform a survey of MTL characteristics and decide upon some requirements for our MTL.

The main advantage and in fact the goal of this approach is that it allows us to focus on the model transformation specific aspects of the language as described in Chapter 3 rather than on aspects that are commonplace in other computer languages. Performing the case study will allow us to determine to what extend this goal can be reached and what other (unforeseen) advantages our method yields. The obvious disadvantage of this method is decreased flexibility in defining our language. The case study will allow us to determine to what extend this actually is a problem and what can be done to counter it.

1.3 Related Work

The language we will be developing is based on the declarative Mistral [21] language for Model trans-

formations to a large extend. The topological sort algorithm used to determine trace dependencies

introduced by Mistral will be at the heart of our language as well. Other important influences are

RubyTL [19] and RGen [29]. RubyTL was the first MTL that we know of that is defined as an DSEL

and RGen is a more recent project that does the same thing but also broadens the scope to a more

complete MDE environment. Incidentally, both of these projects use the Ruby programming language

as their host language.

(12)

1.4 Contributions

This section summarises the contributions made by this research. They are described in more details in Chapter 8.

• Architectural Improvements TRans’s architecture offers a very clean and extensible approach to model transformation. It allows the model querying and instantiation mechanisms to be a separate DSEL that is all but completely decoupled from the actual model transformation DSEL.

• Comprehensive Approach Because TRans is so indifferent to the interpretation of the model concept everything can be a model whit its own mechanisms for instantiation and querying. This means that any type of computer data, like for instance a code repository can be wrapped and used as a source or target model.

• AOP Functionality for Model Transformation TRans features the ability to apply Aspect Oriented Programming in its model transformations. This ability is especially powerful since the full featured Ruby language can be used to write advice.

• Complete Survey of DSEL Techniques This report presents a very complete survey of DSEL creation techniques in the context of the Ruby language. Many of these techniques have been applied for the creation of the TRans language which leads to it being a more concise DSEL for model transformations than RubyTL.

1.5 Outline

We start by provide a more complete analysis and decomposition of the problem and the approach we just introduced in Chapter 2. To provide context, we will describe different aspects of MDE and directional declarative MTL’s in particular in Chapter 3. Chapter 4 will give more context on the Ruby programming language and its facilities for creating DSEL’s in particular. If the reader is already familiar with MDE and DSEL creation in Ruby, these chapters can be skipped. Chapter 5 introduces our case study of developing of a MTL as a DSEL, which we will call a Model Transformation Embedded Language (MTEL). The architecture and design of the language is detailed in Chapter 6 in the form of development guidelines, dependencies, a logical overview, class diagrams and sequence diagrams. We evaluate the usability, flexibility and performance of our language in Chapter 7. Finally, we present our conclusions in Chapter 8. Possible paths for reading through these chapters are depicted in figure 1.1.

Chapter 3 Model Driven Engineering

Chapter 1 Introduction

Chapter 4 Ruby and DSEL's

Chapter 2 Problem Analysis

Chapter 5 TRans: A MTEL prototype

Chapter 6

TRans: Architecture and Design

Chapter 8 Evaluation

Chapter 9 Conclusions

Figure 1.1: Reading guide for this report

(13)

Chapter 2

Problem Analysis

In this chapter we decompose the problem stated in our introduction into subproblems and describe our approach to solving to those problems on a high level of abstraction. First, lets reconsider our problem statement:

Designing a MTL from scratch is a time consuming endeavor that requires re-implementation of many language features that already exist (mature and thoroughly tested) in other programming languages.

The obvious solution to this problem is the reuse of language features from an existing programming language for the development of a MTL. This approach provides the MTL with mature and tested implementations of those language features which improves its quality and allows more focus on the development of model transformation specific language features. We refer to the programming language that is reused for implementation of the MTL as the host language. We have identified five ways in which a programming language can be reused to create a MTL:

• Design the MTL from scratch, reusing source code from the host language implementation for the common features.

• Refine the host language implementation to be model transformation specific.

• Design the MTL as an extension to an existing programming language that allows this.

• Design the MTL from scratch and compile it to the host language, leaving common language constructs intact, essentially forwarding them to the host language.

• Design the MTL in the host language as an Domain Specific Embedded Language (DSEL).

The possibilities are ordered from maximum flexibility and minimum reuse to minimum flexibility and maximum reuse, meaning that they require a descending amount of effort for the development of the MTL. This inverse correlation between reuse and flexibility might seem counter intuitive. However, by reuse we mean reusing parts of a single host language. In this case there is only a single alterna- tive for every component you decide to reuse and these components contain certain decisions in their implementation that you won’t be able to make yourself.

We decided to implement our MTL as a DSEL in the our host language because this approach allows maximum reuse and requires the least amount of effort. This means that if this is a viable solution, it is the most desirable one. After making this choice, our new problem is how to develop a MTL as a DSEL in a host language, enabling maximum reuse of the host language features, and requiring a minimum amount of compromise in our MTL because of the lack of flexibility. This problem raises the follow questions:

Q1 What host language should we use and because of which requirements?

Q2 Which facilities in the host language can be used to create a DSEL?

Q3 What features do we want our MTL to support in order to make it a valid proof of concept?

(14)

Q4 To what degree where we able to achieve the reuse we where aiming for?

Q5 What advantages and disadvantages did the achieved reuse yield?

Q6 How does our implementation compare practically to implementations using the same ap- proach?

The next sections present high level descriptions of our solutions to each of these research questions.

2.1 Host Language Choice and Features (Q1 and Q2)

In order to be able to develop a viable MTL as a DSEL, we chose the following requirements for our host language:

• It should have a flexible syntax that isn’t exotic in the context of model transformation.

• It should provide extensive features for the creation of DSEL’s. In particular closures and operator overloading in order to implement OCL.

• It should provide syntax for declarative and functional programming as these paradigms are com- mon in model transformation languages.

• It should have a mature standard library in order to deal with data structures, strings and file formats.

It turns out that even for these straightforward requirements, there is only a single language that’s the obvious choice. We chose Ruby as our host language because it is, to the best of our knowledge, the only mature language that meets all these needs and because it has proven itself repeatedly in the area of DSEL creation. Examples of this in the field of MDE are RubyTL [19] and RGen [29]. We provide an extensive investigation into Ruby DSEL creation in Chapter 4, in order to determine exactly which facilities it offers for this purpose.

2.2 MTL Features (Q3)

In order to establish a feature set for our language that makes it valid proof of concept we performed a literature study on MDE and more specifically an analysis of commonalities and variability’s in model transformation specific language features. This analysis is presented in Chapter 3 and a selection of these features is presented in Chapter 5. Aside from model transformation specific language features, the MTL will also incorporate many common features that it borrows from the host language such as control structures, expressions, a type system, arithmetics, modularisation and the standard library. We spend less time on these because they basically come for free when choosing this approach.

2.3 Evaluating the degree of reuse (Q4)

Reuse is a simple concept; develop (sub)systems and use them across multiple, larger (sub)systems [2] [11].

In the case of DSEL creation, the (sub)systems we plan to reuse are classified as language features, in a very broad sense. A language feature can be a specific language construct like exception handling but also something with a broader scope like a parsing or a method dispatch mechanism. What we’re really doing when developing a DSEL is reusing an entire existing language and building a new language (the DSEL) on top of it. In this sense, the DSEL is an extension of the host language, reusing every aspect of it. Even while every aspect of the host language is going to be present in the DSEL, it is possible to hide some aspects or discourage users from accessing them. In our opinion, the best way to assert the degree of reuse that can be achieved is to develop a prototype. Chapters 5 and 6 describe the development of this prototype and Chapter 7 evaluates the degree of reuse that was achieved that needed to be made.

It is hard to state exactly the decrease in complexity and effort by reusing an existing language because

there are so many variables involved. For a proper comparison you need two functionally equivalent

MTL’s, one developed as an DSEL, and one developed in the traditional way. Since we don’t have these

two languages, the motivation of the statements in our evaluation can only be a logical one.

(15)

2.4 Advantages and Disadvantages (Q5)

If we establish that a certain degree of reuse can be achieved, this will directly or indirectly impact a lot of characteristics of our language implementation in comparison to a language developed in the traditional way. We expect that the characteristics that will be impacted are the following:

• Implementation effort The effort required for the implementation of our MTL is expected to decrease with an increased degree of reuse. This statement only assumes that reusing something requires less effort than recreating it, something we feel is safe to say. This decrease in effort is considered desirable.

• Flexibility We define flexibility as the degree of freedom in implementing the MTL that the lan- guage designer has. When language features are reused from the host language, flexibility is expected to decrease. This assumption is based on the notion that reused language features in- evitably contain design decisions. When unable to make all design decisions that go into a language yourself, your degree of freedom in creating that language is decreased. This decrease in flexibility is considered undesirable.

• Familiarity Familiarity is considered as the amount of knowledge available about the MTL amongst new users. We expect familiarity to increases with increased reuse, given that features are reused from a well established language such as Ruby. Familiarity is considered desirable.

• Maturity and Reliability Maturity and reliability are defined as the density of defects in the MTL. We expect it to increases where reuse is increased as well, given that features are reused from a mature reliable language such as Ruby. Maturity and reliability are considered desirable.

• Library Availability When a language is developed as a MTEL, host language libraries become available to that language as well. We consider the ability to use libraries when writing model transformations desirable.

• Maintainability and adaptability Maintainability and adaptability the effort required for re- spectively repairing defects and making changes to a system. We can assume that a small code base of a certain quality is more maintainable and adaptable than a much larger codebase of equal quality because changes in large codebase potentially have more impact. We therefore assume that these characteristics are increased for the language implementation where implementation effort is decreased. Obviously, maintainability and adaptability are desirable qualities of a system.

• Superfluous functionality When a language is developed as a MTEL, you inevitably reuse language features you don’t actually want to reuse. Some of those features can be useless or even harmful in a model transformation domain. The existence of useless or harmful features in a system is considered undesirable.

Chapter 7 evaluates whether or not these characteristics where actually affected in the ways we expected. If so, this defines in which situations the DSEL approach is preferable based on which char- acteristics are important. Figure 2.1 shows a diagram of the different characteristics we defined, how we expect them to be affected by reuse or one another and whether we consider it desirable for them to increase.

2.5 Comparison to other implementations (Q6)

This research is not the first effort to develop a MTL as DSEL. RubyTL [19] is another one. It is important do evaluate the differences between our implementation and theirs. We will do so in Chapter 7.

2.6 Conclusions

In this chapter we analysed our problem and defined our research questions. We also immediately

answered one of the research questions because there really is only one obvious answer. For the other

research questions we formulated our strategy towards answering them throughout the rest of this report.

(16)

Implementation Effort (bad)

Flexibility (good) Familiarity (good)

Maturity and Reliability (good) Library Availability

(good)

Maintainability and Adaptability (good) Superfluous

Functionality (bad)

Reuse

decreases

decreases increases

increases

decreases increases

increases

Figure 2.1: How we expect different characteristics of our implementation to be affected by increased

reuse

(17)

Chapter 3

Model Driven Engineering

This chapter is an introduction intro the domain of MDE. Section 3.1 introduces the central concepts in MDE in order to provide context. Section 3.2 provides a formal definition of the most important concept of model. Sections 3.4 and 3.5 respectively describe the advantages that MDE promises to provide and the problems that are associated with it. Section 3.6 describes current tools and languages. Section 3.7 describes the features of these languages with special emphasis on commonalities and variabilities in order to identify important features for our own Model Transformation Language (MTL).

3.1 Conceptual Overview

The main concepts in MDE are models, meta-models and transformations. Models are the first class entities of any MDE environment.

A model is a representation of its subject, describing only the relevant aspects of it and omitting everything else. While a model’s subject is represented by the model, the model can be interpreted to obtain information about its subject. While we prefer this terminology, the relation between a model and its subject is also often described as the model modelling its subject and the model being an abstraction of its subject. It’s possible to have multiple models of the same subject, each describing different, possibly overlapping aspects. Additionally, it is possible for a model to be the subject of another model itself.

The relation between a model and its subject is shown in figure 3.1.

model * 1 subject

interpretation representation

Figure 3.1: The relation between a model and its subject

Meta-models essentially define a subset of all possible models by specifying constraints on the them.

In MDE, meta-models are often models themselves, and that way a meta-model for meta-models can be defined, which is called the meta-meta-model. A MDE environment has just one of these meta-meta- models, defining the subset of all models that classify as meta-models. A way to look at this is that the meta-meta-model is the type or class of meta-models, which in turn are the type or class of individual models. Another common terminology is that each model is an instance of its meta-model and that every meta-model is an instance of the meta-meta-model. Figure 3.2 shows these relations.

As mentioned earlier MDE is also concerned with transformations between models. These transfor-

mations are expressed in a Model Transformation Language (MTL). Section 3.7 describes many aspects

of these MTL’s. In most MDE toolsets, models are used to represent these model transformations, this

is a clean approach that allows transformation of transformation definitions. In this case the syntactic

part of the MTL is a meta-model. Figure 3.3 provides an overview of this process and its terminology.

(18)

model * 1 meta-model

type/class of instance of

meta-meta-model

* 1

type/class of instance of

Figure 3.2: Models and their meta-models

models meta-models

meta-meta-model

meta-model for MTL

A to B (transformation definition)

meta-model B meta-model

A

model 1 model 2

(transformation execution) source/target instance of

dependency

source target

Figure 3.3: An overview of the process of model transformation and its terminology

While theoretical understanding about modelling and transformations is vital for the development of a MDE environment, models are a very simple concept from a pragmatic or implementation point of view. In this context a model can simply be viewed a simple data structure, traditionally often a graph with a map of key/value pairs associated with each node. These kinds of models are especially well suited to describe structural aspects of software. UML [17] is a good example of this.

Apart from model to model transformation, it’s also important to perform transformation between models and other artefacts. These other artefacts will often include graphical representations and textual representation such as code. We call creation of models from other artefacts reading, the most common applications of which are loading a model from an editable graphical representation or other serialised form. We call creation of other artefacts from models writing, the most common applications of which are saving a model to an editable graphical representation or other serialised form and code generation.

3.2 Definition of Model

The first defining characteristic of a model is that it is a relative concept [21]; every model exists in relation to its subject. The model represents its subject and can be interpreted to obtain information about it. The model is therefore an abstraction of its subject, describing relevant aspects of it and omitting everything else.

Model interpretation can be described by the mapping of a model to the set of all possible subjects

it could represent. This set of subjects is the subset of all possible subjects that have the characteristics

that the model describes in common. When you look at the floor plans of a house for example you

can derive certain aspects of it (room configuration, sizes, etc.) but others are still variable (location,

(19)

materials, etc.). In this example the floor plans are a model, and all possible houses they could result in are the set of subjects the model could represent. From the resulting set of subjects, a single subject can be chosen based on its non-common (variable) characteristics.

Model interpretation is reversible which means a model can be derived from any of its possible subjects, we call this process subject representation. The other way around however, a single definitive subject can not be derived from a model without extra input. An exception to this rule only arrises if a model describes every single aspect of its subject. We argue however that such a model isn’t a model at all because it isn’t an abstraction of its subject. This point of view is reflected in our final definition of model. Furthermore we can classify model interpretation as an non-destructive process and subject interpretation as a destructive process.

Another import aspect of a model is that it’s expressed in a modelling language. The modelling language often consists of formal syntactics in the form of a meta-model and informal semantics. Most of the rest of this report is devoted to the semantics of model transformations, which are a specific class of models.

Based on the literature [21] and these observations, we chose the following definition of model:

A model is a representation of its subject expressed in a modelling language. It can be inter- preted to derive a proper subset of its subject’s characteristics.

3.3 MDE or MDA?

MDE was proposed as a generalisation of MDA, the original standard from the OMG [26]. The main difference is that MDA focusses on Platform Independent Models (PIM) and Platform Specific Models (PSM) and also on the set of OMG standards, among which is UML. Also, MDE focusses on the development process much more than MDA does. Since MDE is broader in scope it relates more closely to a general purpose MTL than MDA.

3.4 Promises of MDE

The original research on what was then called MDA focussed on a specific case of separation of concerns (PIM/PSM). They where aiming to separate the concerns of functionality and implementation platform.

MDE is an effort to tackle the problem of separation of concerns in a more general sense. Promises of increased maintainability and better communication are closely related to proper separation of concerns.

If MDE will ever achieve true separation of concerns in complex software systems it will deliver on these other promises as well.

3.4.1 Separation of Concerns

The obvious way MDE tries to achieve separation of concerns is by delegating each concern to a separate model. The challenges arise when attempting to assemble models into intermediate artefacts and ulti- mately into working software (transformation). A balance has to be found between describing system knowledge with models and embedding it in the transformations themselves. To have a separate model for every bit of system knowledge would achieve full separation of concerns but would result in a very complex system of models and transformations which would be hard to standardise. Embedding too much system knowledge in the transformations leads to concern tangling and decreases reusability of transformations. The current approach to this dilemma is having a different model driven approach for each system, further decreasing reusability of models and especially transformations.

3.4.2 Better communication

Separation of concerns leads to better and clearer code and models that in turn leads to more effective communication through code and models. Apart from that, there is also the separate notion of models being more easily readable by nature than code and thus being more adequate tools of communication.

However, it might very well be that this notion has risen from the fact that models generally contain a lot

less knowledge and complexity than code does. Models are inherently decoupled from their representation

(20)

though, which opens up interesting prospects of communication through possibly different representations of models.

3.4.3 Maintainability

Separation of concerns is the most important aspect that makes a system maintainable. Change decisions target functionality rather than code and by localising that functionality to a specific part of the system change impact can be limited, thus achieving increased maintainability. The rest of this claim is based on improved communication through models, which a questionable claim by itself. Easily readable artefacts would however facilitate the efficiency of developers who are inexperienced with those artefacts. When it’s easy to go back to a system after an extended period of time and make changes or bring in new people to do it, that system is more maintainable.

3.5 Problems of MDE

The main problems of MDE are those of the models themselves. A unified set of modelling languages that can completely and precisely describe any software system simply isn’t available, the most significant attempt is UML. Another problem of MDE is the poor and often clunky tool support that is solely due to lack of adoption.

3.5.1 Behavioural Modelling

Current software modelling practices, namely the use of UML, are fairly adequate at modelling the structural aspects of a software system. However, there is a big gap as far behavioural modelling is concerned. An important reason for this is the lack of formal semantics for modelling languages. While over half of the UML model types aspire to some kind of behavioural modelling, interpretation and interaction of these types of models is often not an exact science. Especially the diagram types that specify by example like the sequence diagram are completely unusable in a MDE environment because they don’t provide a full specification that includes all corner cases. MDE approaches are currently focussed on the structure of software systems, in which the behaviour is often defined manually using programming code. Behavioural models with strictly defined semantics will have to be developed in order to overcome this problem.

3.5.2 Modelling of Dynamic Languages

Another shortcoming of the dominant modelling language UML is that there are absolutely no facilities for modelling systems that make use of dynamic languages. This deficiency is related to the behavioural modelling gap because part of the behaviour of a dynamic program can be to change or extend its own structure. Runtime specialisation and creation of classes and runtime (re)definition of methods are just some of the things UML can’t model at all. UML is not very unified in the sense that its main focus on effectively modelling Java systems and after noticing the package diagram one might even argue that Java Modelling Language would be a more appropriate name for UML.

3.6 Tool Support

Most MDE tools are build on the Eclipse Modelling Framework (EMF), examples of this are ATL

and Borland Together. The EMF platform is a modelling framework and code generation facility for

building MDE tools. EMF uses XMI to represent models and provides a basic graphical XMI editor

along with tools for loading the models into a Java runtime environment where they can be traversed

and updated. The meta-modelling architecture of EMF is called ECore. In this section we provide

background information on some of the model transformation languages already in existence that are

relevant to our research.

(21)

3.6.1 Query/View/Transformation (QVT)

QVT [27] is a standard created by the Object Management Group (OMG) of which several partial implementations exist, the most notable of which are SmartQVT and the implementation in Borland Together. The QVT standard actually consists of three DSL’s named QVT Relations, QVT Core and QVT Operational Mappings. Relations and Core are declarative languages which both are extended by the Operational Mappings language which also includes some imperative features.

3.6.2 ATLAS Transformation Language (ATL)

ATL [5] is a slightly more pragmatic approach to model transformation because it originated as an implementation instead of as a standard. It originated later than QVT and has always supported many- to-many transformations. Like QVT it operates on models that comply to the MOF meta-meta-model.

3.6.3 RubyTL

RubyTL [19] is a MDE approach which uses Ruby DSL’s for model transformation and code generation.

It is distributed as part of the Agile Generative Environment (AGE) which is an Eclipse environment for MDE. It supports ECore and EMOF which makes it compatible with other MDE solutions based on Eclipse.

3.6.4 RGen

RGen [29] is a much more recent solutions to MDE using Ruby tooling. It was developed for commercial use and also integrates with ECore and provides code generation. It was developed in isolation from RubyTL and has focusses more on meta-modelling rather than transformations.

3.7 Features of MTL’s

This section describes the commonalities and variability’s of MTL’s. These are things every MTL designer must think about in order to specify a new MTL. Many parts of this section where taken from literature [9] but some where stumbled upon during development for this thesis. This section is focussed on declarative directional rule-based MTL’s because they are the most common and are the focus of this research.

3.7.1 Specification Mechanism

The specification mechanism refers to the language used to specify the actual model transformations.

While transformations in most MTL’s are specified using a custom syntax, it is also possible to build on a existing language that has a flexible syntax. An example of this is RubyTL [19], which builds on the general purpose programming language Ruby. Also, it’s easy to imagine specifying a MTL in a general purpose data definition language like XML as another alternative in this area.

3.7.2 Rule, Source and Target Multiplicity

The basic building block of a declarative MTL is the rule. It specifies a mapping between source model

elements and target model elements. Each rule has zero or more source expressions. Source expressions

are evaluated to produce a result that functions as input for the rule. While this result is often a bunch

of model elements it can also be something simple like a string or a number. It is possible to have

multiple source expressions and combine their results somehow. The most common combination method

is a cartesian product, but other possibilities are a set union, a set intersection or having a configurable

combination method for each rule. The more elegant and powerful solution however, is to have a single

source expression for a single rule and allow these set operations and the querying of multiple models

in that single expression. Another valid but uncommon possibility is to allow rules without a source

expression at all, in which case the rule would have no input. The single rule, single source alternative

(22)

can however easily mimic this behaviour by having the single source return a void object (nil or null in most common languages).

While the source expression of a rules functions as its input, the target of a rule functions as its output or result. It creates, changes or deletes an element. Usually, target(s) are evaluated once for each item in the input. While a rule without any targets would make no sense, both allowing just a single one and allowing multiple are valid approaches. Allowing only a single one does not result in decreased functionality compared to the alternative and is thus the cleaner approach. Experience has thought us however that allowing multiple targets for a single rule is often convenient, because it can reduce redundancy in source expressions without the use of helper rules.

3.7.3 Source, Target or Hybrid Models

The models a transformation is concerned with often have specific roles. Some models can serve strictly as input, which means they can be queried(read) but can not be changed(written). Other models might serve strictly as output models, which means they can are generated(written) during transformation but may never be queried as the source of a rule. A MTL can also allow a hybrid form, which is model that can be both queried and changed. However, this is a controversial feature because it inevitably leads to non-determinism in declarative transformations. Hybrid models are desirable though because they facilitate in-place transformation, which allows for much simpler transformations for model refinement.

An alternative way to achieve more simple model refinement transformations is to include powerful model element copying features in the MTL.

3.7.4 Tracing Facilities

The advantage MTL’s have over the more common tree-based transformation languages like XSLT [6]

is the ability to transform graphs that have cycles. MTL’s accomplish this by offering tracing facilities.

The basic idea is to keep a record of all the elements that where created and what source data was used to create them. This information can then be queried in order to create the target element.

A classic example is the transformation of ’properties’ that have a ’class’ as owner to ’columns’ that have a ’table’ as owner. When transforming ’columns’ to ’properties’ in this case, the transformation can use trace data to find the ’table’ that was generated from the ’class’ that owns the ’property’ to serve as the owner of the ’column’. Figure 3.4 shows that the columns could have never found its owner without the trace information.

Class

Property

Table

Column trace

trace

owner owner

source model target model

Figure 3.4: Example of keeping a trace information

3.7.5 Transformation Ordering

Even though declarative transformation don’t have explicit rule ordering, rules do sometimes need to be evaluated in a specific order to complete a transformation. The case presented in Section 3.7.4 is an example of this. The rule that transforms ’classes’ to ’tables’ needs to be evaluated first in order for the rule that defines the ’columns’ to find those ’tables’ using trace information and set them as the ’column’

owner. In a purely declarative transformation language, the transformation engine is responsible for

(23)

determining this ordering. This ordering can be determined in an extra pass that is done before the actual transformation pass, but can also be done on the fly. In the second case the transformation can just start with the evaluation of any rule and evaluate other rules as they are needed.

While it is common to evaluate rules atomically (entirely or not at all), a more powerful approach is to do the same for rule targets or even target elements. These approaches allow tracing dependencies between targets of the same rule or between target elements of the same target respectively. An example case in favor of the approach that allows dependencies between target elements of the same rule target is the transformation of an inheritance hierarchy using a single rule.

Though we can transform entire tree structures in a single rule with this last approach, we can’t do the same for graphs that contain cycles. This can however be done by forgetting about ordering altogether and propagating all the trace information queries to an extra pass that is executed after the main transformation pass. However, this approach poses a problem when the transformation wants to traverse the result of a trace query.

Transformation Order Control

When non-determinism is introduced into a model transformation because it manipulates a hybrid model, the need for rule order control arises. Several mechanisms have been invented to allow this. The first and simplest is to just specify the order in which rules have to be evaluated. This is very much at odds with the declarative nature of these transformations and it is not very well maintainable. A slightly more desirable approach is to group rules and specify the execution order of those groups, a mechanism often referred to as transformation steps. However, this approach suffers from exactly the same deficiencies as the first one. A less flimsy and more maintainable solution is to allow rule dependencies to specify ordering only where necessary. Nonetheless, all these alternatives are considered harmful because they severely interfere with the declarative nature of transformations.

3.7.6 Source and Target Model Multiplicity

All MTL’s need to decide how many input and output models to accept and generate. The possibilities are to support one-to-one, many-to-one or many-to-many transformations. The one-to-one alternative is very constricting and so not very common, it means the MTL would never be able to combine mul- tiple models into one. Supporting many-to-one transformation is the cleanest way of opening up all possible transformation scenarios because it can achieve many-to-many transformation by having a sep- arate transformation for each of the output models. Many-to-many transformations open up possibilities for tangling within transformations because multiple unrelated models could be generated by a single transformation. However, this is the preferred alternative for most MTL’s because it allows genera- tion of side-products for a transformation like log and trace data. Especially trace data is crucial for implementing incremental or partial transformations.

3.7.7 Source Expression Language

This part of the MTL is subject to reuse and standardisation because it is far more tightly coupled with the modelling standard used than with the MTL itself. A mature MTL should make it easy to swap out one expression language for the other and could even allow multiple languages to be used in parallel.

The most common standard in this area is OCL, which is a standard from the OMG [4].

Object Constraint Language (OCL)

OCL is a standard that was developed by the OMG to query UML models. Apart from some basic

types and operation, its distinguishing features are the support for multiple types of collections and

set operations on them. Included in these set operations are existential and universal quantifiers with

closure support, a very useful feature in this domain better known from SQL and dynamic programming

languages.

(24)

XQuery

XQuery [3] is not commonly applied in the area of model transformations. It does however deserve a mention as the dominant standard in the area of document querying. While XQuery is a standard for querying XML documents that are essentially trees, it contains several features that could drive innovation in the area of model querying, the most notable of which being XPath.

3.7.8 Helpers Functions

Most MTL’s have support for helper functions, often referred to as helper rules. These are totally different from the rules discussed earlier and hence the term function is preferred. While rules are always evaluated once and just once, helper functions can be invoked at will from these rules. Like any function they serve to contain functionality that is used in multiple places and that would otherwise have to be duplicated. The other important reason to have helper functions is to make a MTL Turing complete by allowing recursion.

3.8 Conclusions

While the research area of MDE is quite extensive, only several concepts are really well established. The

well established commonalities in the area of model transformation are the concepts of source and target

models, transformation and helper rules, rule ordering mechanisms, tracing mechanisms and declarative

definition of transformations. Apart from the existence of these concepts in almost every MDE approach,

there is a lot of variability in the interpretation of these concepts among different MDE approaches.

(25)

Chapter 4

Ruby and Domain Specific Embedded Languages

In this chapter we discuss the aspects of Ruby that make it great for the development of Domain Specific Embedded Languages (DSEL). We start by providing introductions into DSEL’s and some of Ruby’s structural and syntactical features. Since the most defining characteristic of Ruby is its marriage of different programming paradigms, most of the rest of this chapter is structured into the programming paradigms that Ruby borrows its language features from. These chapters focus specifically on how these language features can be applied to develop DSEL’s.

Section 4.3 provides some introduction into the Ruby syntax. It focusses on syntactic aspects that make it great for DSEL development. For a broader and more thorough introduction we refer to the online documentation [25]. Sections 4.4, 4.5, 4.6 and 4.7 respectively describe object oriented, prototype oriented, functional and aspect oriented features of the Ruby language that can be used for DSEL creation.

4.1 Historical Context

Ruby [25] is a dynamic, reflective, general purpose, object oriented programming language. Ruby origi- nated in Japan where it was developed by its creator Yukihiro ’Matz’ Matsumoto during the mid 1990’s.

While Ruby’s internals bear the most resemblance to Smalltalk [1], the Perl [14] programming language has served as a strong inspiration for the exterior features of Ruby. Matz had already picked the name Ruby over Coral before any code for the language was written. It was realized later that the Ayurvedic birthstone system designates the pearl as the birthstone for the month June and the ruby as the birth- stone for the month July, implying Ruby as the successor of Perl. Ruby received a lot of attention in recent years because it is the foundation of the web development framework Ruby on Rails [10].

Ruby has never had an official specification. The original C implementation created by Matz is regarded as the authoritative specification for the language. Recently, third parties have started creating their own implementations of Ruby. Rubinius [28] was the first alternative implementation. It uses a custom virtual machine and is supposed to be faster and more reliable than the original implementation.

However, it has yet to gain popularity. The RubySpec [22] project originated from the Rubinius project

and is an executable specification of the Ruby language that was written in Ruby itself. RubySpec is

now the standard for checking compatibility between different implementations. Sun is supporting the

development of JRuby [13], which runs on the Java Virtual Machine (JVM) [24]. The main advantages

of JRuby are its speed, its ability to integrate with Java libraries and its ability to deploy Ruby on Rails

web applications to Java application servers. Microsoft also announced its own Ruby version for the

.NET framework [8] called IronRuby [7], which is currently in alpha.

(26)

4.2 Domain Specific Languages

A Domain Specific Language is a computer language that is tailored to a specific application domain.

It allows programs written in it to be more effective and concise within this application domain. This is accomplished by providing these programs with an implicit context that would have to be provided explicitly if the program was written in a general purpose computer language. While a DSL can be a language in the traditional sense with its own parser and compiler or interpreter, we are focussing on Domain Specific Embedded Languages (DSEL). A DSEL is a DSL that is embedded in a suitable general purpose computer language, borrowing common language features from it and adding language features that are lacking. A DSEL can be embedded in its host language at compile time [18] or at runtime [12]. We are focussing on the variant that is embedded at runtime, which is essentially an advanced form of the Application Programmer Interface (API). The distinction between API’s that can be called DSEL’s and API’s that can’t is very vague. We use the term DSEL very broadly to describe any API that has DSL-like properties. Embedding at runtime means that the DSEL has no control over its own compilation and thus that only language constructs of the host language can be used to express programs in the DSEL. Even though this approach seems constricting at first, this chapter will show that a well designed host language can require conformance from a DSEL while still being very flexible.

4.3 The Ruby Syntax

The Ruby syntax focusses on flexibility and conciseness. Many of the core constructs in Ruby can be expressed in several different ways, allowing it to mimic other computer languages. The code sample in figure 4.1 shows two pieces of code that do exactly the same thing using different programming styles, both are valid Ruby code but the first shows syntactical similarities to functional programming and linux shell scripting while the second one is similar to C and Java. This example shows two different syntaxes for both code blocks and string literals and also shows the optionality of both parenthesis for method invocation and the semicolon for statement termination. While these are only minor exterior provisions in the Ruby parser, it shows a design philosophy that prefers to allow rather than to require.

1 # Print 'Hello World' three times 2 3.times do

3 p 'Hello World' 4 end

5

6 # Exactly the same, but different 7 3.times {

8 p("Hello World");

9 }

Figure 4.1: Example of Ruby syntax

Another example of this is illustrated by the code sample in figure 4.2. The first few lines define a method that takes one required argument and one optional argument that defaults to an empty hash.

The last few lines show different ways in which this method can now be invoked. The important thing to note here is that wherever the hash literal character ’=>’ is used in a method invocation, Ruby will attempt to parse the rest of the argument list as a hash and pass it as a single argument, even though they are not grouped explicitly. While allowing to omit these hash grouping characters is again only a minor and rather quaint bit of syntactic sugar it allows concise syntax and flexibility in method definitions.

The colon characters in this example are simply the literal syntax for creating a symbol. Symbols are similar to strings and are often used as the keys in a hash. Symbols don’t contain whitespace and are more lightweight than real strings because they are represented internally by an integer. The last line shows an alternative syntax for hash literals introduced by Ruby 1.9.

The ability to express language constructs in different ways is especially useful for the creation of

DSEL’s using Ruby. It allows the user (programmer) of the DSEL to pick an alternative that is suitable

in the context that the DSEL provides to improve readability. Please note how closely the last line of the

code sample in figure 4.2 mimics SQL, a DSL for querying relational databases, while still being Ruby.

(27)

1 # Define the find method 2 def select from, options = {}

3 # Perform sql query 4 end

5

6 # Invoke method with different arguments 7 select 'products'

8 select 'products', :where => 'price > 100'

9 select 'products', :where => 'price > 100', :order_by => 'name' 10

11 # Using alternative syntax for hash literal (since Ruby 1.9) 12 select 'products', where: 'price > 100', order_by: 'name'

Figure 4.2: Example of using named arguments

4.4 Object Oriented Programming Features

Ruby is a purely object oriented language. Without exception, everything in Ruby is an object and there are no primitive types. This choice provides a lot of simplicity and consistency throughout the language and eliminates the need for hacks such as Java’s auto-boxing. It means that all literals are objects as well and methods can be invoked on them. Some code examples of this that are commonly used to advocate the Ruby language can be found in figure 4.3. The ability to invoke methods on literals is directly derived from the ability to instantiate certain types of objects using a literal syntax. Besides the strings and integers, the Ruby language provides literal syntaxes for instantiation of floats, character codes, symbols, ranges, arrays, hashes, regular expressions and even shell commands.

1 5.times do

2 p "World! Hello".split.reverse.join(' ') 3 end

Figure 4.3: Examples of calling methods on literals

Another important thing to realize about Ruby is that every object has a class and these classes themselves are also objects. Classes are instances of the class called Class which in turn is an instance of itself, terminating the instantiation hierarchy. Ruby also has the concept of modules, which are basically classes that cannot be instantiated. Modules are often referred to as abstract classes in other languages.

While Ruby does not support multiple inheritance and so every class has a single superclass, multiple modules can be included into the definition of a class. This behavior mimics multiple inheritance while eliminating some of the inconsistencies it causes. From here on we will refer to the class called Object, the class called Module and the class called Class as the meta-object, the meta-class and the meta-module respectively. We refer to the set of all three as the plural meta-objects because they all inherit from the meta-object and to avoid the overloaded term meta-classes. We will define meta-programming as programming using these meta-objects. The meta-module is located in the inheritance hierarchy between the meta-class and the meta-object, which signifies the root of inheritance hierarchy. All these relations are shown in figure 4.4 as the results of calls to the methods class and superclass, respectively defined by the meta-object and the meta-class.

Figure is 4.4 particularly useful for understanding the instantiation and inheritance hierarchies that where mentioned in the previous paragraph. The instantiation hierarchy is signified by the ’class’ re- lations in this figure, while the inheritance is shown by the ’superclass’ relations. Please note that the instantiation hierarchy exists throughout all Ruby objects while the inheritance hierarchy only applies to classes. This model allows Ruby to use a very simple mechanism for method dispatching: go one step up the instantiation hierarchy and then start looking for the method up the inheritance hierarchy. If the original method isn’t found the same mechanism is used to look for a method named ’method missing’

which is implemented in the meta-object (the root of the inheritance hierarchy) to throw a runtime

’MethodNotFound’ exception.

4.4.1 Open Imperative Class Definitions

Even though simple Ruby class definitions look a lot like declarative class definitions in for example Java,

class definition in Ruby is actually an imperative process. This makes it possible to use control structures

Referenties

GERELATEERDE DOCUMENTEN

Op twee achtereenvolgende dagen (10 en 11juli) kregen de deelnemers 4 uur voor drie opgaven. De maximale score per opgave bedroeg 7 punten. In vergelijking met voorgaande jaren was

Nagenoeg alleen voor onderzoek waarbij met grote groepen dieren gewerkt moet worden, komen praktijkbedrijven in aanmerking.. Gedragsonderzoek bij kippen onder praktijk

En dat nieuwe betreft niet alleen de Hollandse beelden, want die beelden staan op hun beurt voor een ronduit be- leden trots dat Nederland en de Nederlandse taal zich vanaf nu

This research focused on small artisanal bakeries. Those companies are part of the Dutch food culture, but over the past decade supermarkets captured the major part of the

His belief in deity was basically subject to the scientific observation that nature obeys laws for its own existence and for that of life (Flew with Varghese 2007:89). He

Only once the consumer of cloud computing has significant control over tangible computer resources, and it is established that this was also the wilful intent of the parties to

Data collection involved naïve sketches (Giorgi 1985: 1) after each workshop in the schools, during which individual partici- pants were requested to present their views on how

The principles of a correct approach to the geriatric patient are thus: (i) determine his needs as exactly as possible; (ii) provide for these requirements; (iii) protect, treat