Design Rules in EScala
Introduction
EScala is a language integrating declarative events in
Scala programming language. It makes it possible to declaratively define and combine events, quantifying over relationships between objects.
EScala aims to enable modelling of event-driven program in a modular way. Here are some thoughts about encoding design rules using the (E)Scala mechanisms.
Possibilities and Limitations
EScala conserves the ideas of object-oriented languages, and can only describe the interface of an object. It cannot enforce the behavior of methods or the absence of some element.
However, some enhancements are planned which may also help to write more complex Design Rules in EScala. See
below for more details and ideas.
Some Design Rules
Some of the structural design rules may be encoded in EScala. This section summarizes a first attempt to encode Health Watcher DRs in the language. The used constructs are explained in the listings.
- Using Scala's abstract type members, we can enforce that a DR instance defines a type implementing a specific interface
- With structural types, no inheritance hierarchy is required for specifying the interface
abstract class TransactionManagementDR {
// the type member ITransactionMechanism represents an abstract type which
// has three methods, begin, commit, rollback
type ITransactionMechanism <: {
def begin()
def commit()
def rollback()
}
// the facade must expose three events corresponding to transactional methods
// theses events are abstract
type HWFacade <: {
evt transactionStarted[Unit]
evt transactionFinished[Unit]
evt transactionAborted[Unit]
}
// this type is a subtype of the ITransactionManagement trait
// in this case, a trait is used to make it possible to have an initialization block
// the initialization block registers the transaction management methods with
// the event of the associated facade
type ITransactionManagement <: TransactionManagement
trait TransactionManagement {
// a ITransactionManagement instance must have a reference to a facade
val facade: HWFacade
val transactionMechanism: ITransactionMechanism
// initialization block (constructor code)
// this is a simple case where the pieces of advice just call the corresponding
// transaction methods. This is a way to fake the design rule enforcing the advice to call
// the transatcion management methods
facade.transactionStarted += transactionMechanism.begin _
facade.transactionFinished += transactionMechanism.commit _
facade.transactionAborted += transactionMechanism.rollback _
}
}
An instance of this design rule may be a singleton object or a normal class. It must specify all things that are abstract. In this case, the three abstract types
object TMDR extends TransactionManagementDR {
type ITransactionMechanism = TransactionMechanism
type HWFacade = SystemFacade
type ITransactionManagement = MyTransactionManagement
}
The three concrete implementation of the type may be dependent types, defined in the DR instance or not.
// no inheritance hierarchy is imposed here, due to the use of duck typing in the abstract type declaration
class TransactionMechanism {
def begin() { ... }
def commit() { ... }
def rollback() { ... }
}
class SystemFacade {
evt transactionStarted[Unit] = beforeExec(this.registerComplaint) || beforeExec(this.registerEmployee) || ...
evt transactionFinished[Unit] = afterExec(this.registerComplaint) || afterexec(this.registerEmployee) || ...
// this feature is still to be implemented in EScala
evt transactionAborted[Unit] = afterThrowing(this.registerComplaint) || afterThrowing(this.registerEmployee) || ...
def authenticate(String login, String password): Employee = { ... }
def registerComplaint(Complaint complaint) { ... }
def registerEmployee(Employee employee) { ... }
...
}
class MytransactionManagement(val facade: TMDR.HWFacade, val transactionMechanism: TMDR.ITransactionMechanism) extends TMDR.TransactionManagement
Modularization Mechanisms
Using mixin composition, the implementation of a DR may be splitted in different traits that are then mixed into the instance.
trait TransactionMechanisms {
// self type is a kind of `requires' this trait can only be mixed with TransactionManagementDR
this: TransactionManagementDR =>
type ITransactionMechanism = TransactionMechanism
class TransactionMechanism { ... }
}
trait BusinessFacades {
this: TransactionManagementDR =>
type HWFacade = SystemFacade
class SystemFacade { ... }
}
object TMDR extends TransactionManagementDR
with TransactionMecahnisms
with BusinessFacades
Possible Extensions to EScala
EScala aims to stay a general purpose language, however some new features may help to define more interesting DRs without being to specific.
- Adding support for afterThrowing implicit events.
- An action (a method in an OO design) consists in a list of steps which are mor or less explicit. These steps could be part of the method signature, exposing more events on the internal structure without breaking modularity. The details of implementation are not shown, only the specified steps of the method.
- Having virtual classes in Scala could also help to refine DRs. For the moment Scala supports a Virtual Class Pattern which achieves the same goal but is quite verbose. An attempt was made to implement virtual classes in Scala, but not finished yet.
Quantifying over classes
An important problem to solve is the quantification over many different instances of a class, or over objects of different classes. There exists no declarative way to do it in EScala for the moment.
Encoding quantification over different instances/classes with the current version
In the example above, if the transactionManagement class observes many different Facade, we could encode it as follows
import scala.events.VarList
...
trait TransactionManagement {
// the VarList class allows to observe a mutable list
// of variables.
val facades = new VarList[HWFacade]
val transactionMechanisms: ITransactionMechanism
// using the any operator, we can observe any occurrence of the
// referenced event occuring in the list
// e.g. the first event is
// if any facade in the facade list triggers its transactionStarted event
// the reaction is called
facades.any(_.transactionStarted) += transactionMechanisms.begin _
facades.any(_.transactionFinished) += transactionMechanisms.commit _
facades.any(_.transactionAborted) += transactionMechanisms.rollback _
}
But we must imperatively registers any new HWFacade in the facades list. This is good if we do not want to observe all existing instances but dynamically add or remove them from the list.
An example of the syntax is showed below
trait TransactionManagement {
val transactionMechanisms: ITransactionMechanism
// by referencing the event on the trait name, we observe all the existing instances
HWFacade.transactionStarted += transactionMechanisms.begin _
HWFacade.transactionFinished += transactionMechanisms.commit _
HWFacade.transactionAborted += transactionMechanisms.rollback _
}
Quantifying over classes
Another solution would be to allow quantification over classes and not over object relationships.
The HWFacade type must be observable. One need a way to indicate that the IHWFacade is
globally observable, that is to say one may observe all the existing instances of this type.
type HWFacade <: IHWFacade
observable trait IHWFacade {
evt transactionStarted[Unit]
evt transactionFinished[Unit]
evt transactionAborted[Unit]
}
(the use of the
observable
keyword here is not definitive, it is a first idea). By using this, we do not loose the OO modularity and can observe all the instances, the class/trait is instrumented when it is compiled.
This mechanism could also mean that one can observe method executions of all instances of a class.
This
observable
modifier for a class/trait would mean, that all events (explicit and implicit) may be observed on all instances. So this code would be a regular code
observable class C {
def m(...) = ...
}
evt e = afterExec(C.m)
--
LucasSatabin - 14 Oct 2010

Copyright © 2008-2023 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki?
Send feedback