Dynamic Software Evolution in the Next Scripting Language
<neumann@wu-wien.ac.at>
June 2012
1. Dynamic Software Evolution
Dynamic software evolution provides a means to change the behavior of a running program on the fly. The research of evolvable systems has already a long history (see e.g. [1]) but they are gaining over the last years more attention. Dynamic code evolution can be used e.g. to "learn" new behavior (adapt to new situation), or to stop erroneous behavior. Dynamic code evolution is particular useful when combined with introspection, i.e. the ability of a programming system to query all its properties (classes, objects, variables, methods) with their current relations. This kind of reading introspection is can be used in reflective systems for a program to obtain information about itself (self-awareness) or to implement persistence and runtime tracing/monitoring. The ability to alter the introspected properties at runtime is sometimes called "write introspection", providing the option for implementing evolution for running software systems. Dynamic Software Evolution leads to evolvable architectures [2].
2. Benefits of Dynamic Software Evolution
-
Dynamic software evolution supports rapid development cycles. Especially for system with long start up times, dynamic modifiability allows rapid modification of code and designs without the need of a restart of the system. This property is particular useful for the modification of (long-running) servers, which do not have to be shut down when a modification is applied. This necessarily for code evolvability is recognized in the Web 2.0 manifesto [3] (e.g. updates of production version the flickr in the half-hour interval; release early, release often).
-
Dynamic software evolution supports adaptive program structure and behavior. In situations, where the environment changes, e.g. class structures can evolve rather than requiring a destroy/create cycle. This is a recognized example from object oriented databases [4] When e.g. a student becomes an employee, it is not necessary to destroy the student object an to create it new with a copy of the old data, the object can simply change its class. Similarly, a system can learn through interactions and adapt either on the object or class level.
-
Dynamic software evolution supports code injections/removals at runtime in the style of aspect oriented programming. This is particular useful for debugging and behavior traces.
3. Dynamic Software Evolution Features supported by NX
3.1. Dynamic State Changes
-
Add/Remove Attributes: All instance variables of NX objects are kept in the objects. The existing instance variables of an object can be queried, new instance variables can be added independently from classes, instance variable can be unset (removed) at runtime.
-
Dynamic Value Types: Tcl support already dynamic data types. Per default, all variables are strings. By performing operations on the values, the values are converted into more efficient internal datatypes (such as e.g. integers). On demand, the string representations of the values are updated. The change between different datatypes is performed dynamically.
3.2. Dynamic Behavior Changes
-
Dynamic Binding: All method lookups in NX happen at invocation time. Other options would have been at compile-type (static binding) or at class/method definition time or object creation time. This strong form of dynamic binding is necessary for supporting dynamic behavior changes.
-
Dynamic Method Definitions: All methods of NX can ne created/modified/deleted at any time. This follows the idea of open object/class definitions. Some languages support closed class definitions, where all methods defined by a class have to be defined at class definition time and can’t be deleted later.
Dynamic method definitions are possible, since NX classes are runtime entities, i.e. they are special kinds of NX objects, which are able to manage the object life-cycles, and which provide instance methods for their instances. -
Dynamic per-object Behavior: NX objects can have per-object methods. Per-object methods can be added/modified/deleted at runtime, independent from the class structure. Per-object methods are used e.g. for the definition of "class methods", which are methods applicable to the class object. On the contrary, instance methods are methods applicable to the instances of an class.
-
Altering Properties of Methods: Methods of NX have properties like e.g. call-protection (public, protected, private) or overwrite protection. These properties can be changed for every method at any time.
3.3. Dynamic Changes of the Class Structure
-
Re-Classing: The relationship between an NX object and its class can be changed at arbitrary times (i.e. as well after the object was created). Therefore, NX objects may change their class at any time. When the class of an object changes, the instance variables and the per-object methods are not modified.
-
Dynamic Class Deletion: Since NX Classes are special kinds of NX objects, it is possible to delete classes at runtime. When a class is deleted at runtime, it current instances are changed into instances of the next higher class in the class hierarchy (maybe the most general superclass nx::Object). At the same time, the class is removed from any sub-/superclass and mixin relationship.
-
Object/Class Redefinition: NX distinguishes between a new definition and a redefinition of an object/class. Per-default NX performs a full cleanup on a redefinition of a class. In an environment with dynamic code evolution, the behavior as stated above might not be desirable, since it might be complex to restore e.g. the class relationships when in an long-running server some classes are reloaded. The behavior of recreate operations can be defined from the script level.
The next scripting framework can be configured to perform keep the relationships upon recreates (convenience configure flag softrecreate). This option is e.g. used intensively in the OpenACS framework where e.g. packages can be reloaded for code evolution at runtime. -
Dynamic Changes in the Class Hierarchy: All relationships between objects and classes are relationships that can be dynamically altered. Therefore, a class can e.g. change at any time its superclasses, or e.g. mixin classes (see below).
3.4. Dynamic Software Composition
-
Ruby/Scala style mixins: Several Scripting Langauges support mixin classes. However, the semantics and therefore the applicability of this idiom for development tasks differs. For example the scripting language Ruby defines a concept named a "module", which can be used to mix-in a predefined behavior into multiple classes. When e.g. a module M is used in a class C, the methods of M are added to C with a lower precedence than methods of C. Therefore, C can refine the behavior of M. Similarly, mixin classes behave in Scala.
In NX this style of mixins can be realized via multiple inheritance, by simply adding M to the front of the list of superclasses of C. NX needs no special language construct. All changes to M (adding methods, altering the class hierarchy of M, …) are fully dynamic in respect to C and its instances. -
Mixins as Decorators: The term "mixin class" in NX is used like specified in the decorator design pattern. The difference is that when e.g. a mixin class M is mixed into a class C, the methods of M are added to C with a higher precedence than the methods of C. Therefore, M can refine the behavior of C.
NX supports-
per-object mixins (the methods of the mixin class hierarchy are applicable to the object, to which the mixin-class was added), and
-
per-class mixins (the methods of the mixin class hierarchy are applicable to the instances of the class, to which the mixin-class was added)
-
-
Transitive Mixins: The per-class mixins are transitive, this means it is possible to extend also the behavior of a mixin class using per-class mixins.
-
Traits: In addition to mixins, NX supports a variant of traits [5]. Traits realize a composition mechanism for the reuse of methods. Contrary to e.g. mixin classes, the methods defined in traits are materialized in the target objects and classes. For every method inherited from a trait the composition behavior can be specified. This gives more fine-grained control over the reuse of methods and overcomes the "total composition ordering" limitation of mixins.
3.5. Dynamic Code Injection (reversible)
-
Filters and Mixins: XOTcl was the first language supporting language constructs for dynamically adding and removing design patterns to existing systems. The primary instruments for this are the so-called filter methods and mixin classes (see above).
NX has the exactly same functionality as XOTcl. These mechanisms can be used conveniently for debugging, tracing, and adapting a running system. -
Guarded Code Injections: The code injection facilities are not only dynamic, but these can be made as well context dependent (a mixed-in method or a filter method is only applied when e.g. certain instance variables have certain values). This idea is similar to the notion of subject-oriented programming [6].
4. References
[1] Robert S. Fabry. How to Design a System in Which Modules Can Be Changed on the Fly. In Proceedings of the International Conference on Software Engineering, pages 470–476. IEEE Computer Society, 1976.
[2] Rank, Stephen (2002) A reflective architecture to support dynamic software evolution. PhD thesis, University of Durham.
[3] Tim O’Reilly: What is Web 2.0: Design Patterns and Business Models for the Next Generation of Software.
[4] Georg Gottlob, Michael Schrefl, Brigitte Röck: Extending Object-Oriented Systems with Roles. ACM Transactions on Information Systems 14(3): 268-296 (1996).
[5] Ducasse S., Nierstrasz O., Schärli S., Wuyts R. , Black A. P. (2006): Traits: A mechanism for fine-grained reuse. ACM Trans. Program. Lang. Syst. 28(2): 331-388 (2006).
[6] William Harrison and Harold Ossher, Subject-Oriented Programming - A Critique of Pure Objects, Proceedings of 1993 Conference on Object-Oriented Programming Systems, Languages, and Applications, September 1993.