Metaprogramming allows the programmer to manipulate basic Libretto entities (packages, classes, functions and fields) as mutable data structures. The tools, which are outlined in this section, can affect the basic object model of the program. Since they are powerful and brutal, it is recommended to use them with great caution and only when necessary.
Libretto is designed to follow the principle: metaprogramming is necessary only for solving ‘metatasks’, that is, those tasks, which really demand code self-modification, or a non-monotonic modification of the object model. In all other cases the regular Libretto tools are more preferable. For instance, if we need to add new method
foo to the existing class CC, it is not necessary to modify the class definition, because we can define foo as an external method:
def CC foo = …Similarly external fields are used, which allow the programmer to add new fields to existing classes:
var CC foo
Entities
The basic entities of Libretto are classes, functions, fields, objects, types and packages. All such entities are instances of the predefined classEntity, which is a superclass of the classes Class, Function, Field, Object, Type and Package.
Some methods are associated with the class
Entity, for instance,namereturns the name of the entityrenamerenames the entitygetObjectreturns the object of the entity- etc.
The entity class hierarchy is shown below. In brackets, some methods associated with these classes are mentioned:
Entity [name, rename, getObject]
Class [create, do, classes, addSubclass, fieldsDirect]
Function [do, undo, addFunctionDef, domain, range]
PartialFunction [isDefinedAt]
ExternalMethod
InternalMethod
AnonymousFunction
Object [do, undo, owners]
Field [addValues, domain, range]
ExternalProperty
Property
Map
InternalProperty
Package [addSubpackage]
Classes
Classes in Libretto are declarative instances of the classClass. To illustrate metaprogramming tools, let us consider some methods associated with classes in more detail.
The method
newClass creates a new class in a certain package (by default – in the current package) with the name passed as the first parameter. For instance,
newClass(“MyClass”)is equivalent to
class MyClassThe method
addSubclass declares a class as a superclass of another class. For instance,
newClass(“C”) newClass(“D”) %C.addSubclass(%D)is equivalent to
class C class D extends CThe method classes with signatures
def Class classes: Class*
def Object classes: Class*
def Package classes: Class*
returns all classes of an entity (a class, an object or a package). For instance,
package b class A class B extends A class C extends B class D object obj extends C, D getPackage(“b”).classes // A B C D %C.classes // A B %A.classes // () obj.classes // A B C DThe method
classesDirect return those classes, which are directly associated with the context value, e.g. direct superclasses of a class, or classes, which the current object directly belongs to (not via inheritance), or the top classes of a package:
package b class A class B extends A class C object obj extends B, C getPackage(“b”).classesDirect // A C %B.classesDirect // A %A.classesDirect // () obj.classesDirect // B CThe method fields with signatures
def Class fields: Field*
def Object fields: Field*
returns the fields, which are associated with an entity (either a class, or an object). For instance,
class C(fix n: Int) class D(fix c: C) class E(fix s: String) extends C, D object obj extends C, D %C.fields // C/n %E.fields // E/s D/c C/n obj.fields // C/n D/cThe method
fieldsDirect returns the fields, which are directly associated with a class:
class C(fix n: Int) class D(fix c: C) class E(fix s: String) extends C, D %E.fieldsDirect // E/sA lot of other methods are associated with classes including
classFieldsreturns all fields associated with the classobjectsDirectreturns all instances created by the class constructorobjectsreturns all instances of the classremoveSubclassretracts the class as a subclass of another classsubclassesDirectreturns all direct subclasses of the classsubclassesreturns all subclasses of the classsubclassofis a filter letting those entities through, which are subclasses of the classnewis the method that calls the constructor of the class.
etc.
Functions
Functions in Libretto are declarative instances of the predefined classFunction:
class Function extends EntityThe method
do determines the behavior of a function. For instance,
def plus(n: Int) = this + nis syntactic sugar for
object plus extends Function {
def do(n: Int) = this + n
}
The notation for anonymous function %(x){x + 1} is syntactic sugar for
Function() # {def do(x) {x + 1}}
Since the class Function is underdetermined, it is always necessary to reify the abstract function do.
Some other methods are associated with function entities, for instance,
addFunctionadds an external function to a packageaddMethodadds a method associated with a classaddFunctionDefadds a new definition to a polymorphic functionremoveFunctionremoves an external function from a packageremoveMethodremoves a method associated with a class
etc.
Fields
Field entities are the instances of the classField. The set of methods working with fields as entity objects, in particular, includes:addValuesadds values to a fielddomainreturns the domain type of a fieldmaxCardreturns the maximum allowed number of valuesaddFixFieldcreates a new immutable field associated with a class or an objectaddVarFieldcreates a new mutable field associated with a class or an objectrangereturns the range type of a field
Objects
Here are some methods working on objects.addObjectClassadds an object to a classfieldsreturns the fields associated with an objectnewObjectcreates a new objectownersreturns objects, a field of which has the current object as its valueremoveObjectClassremoves an object from a classremoveValueremoves a value from an object fieldremoveValuesremoves all values from an object fieldsetValuesassigns new values to an object fieldvaluesreturns all values of an object field (or of all fields of the object)
Types
The types are used for typing entities and solving some other metaprogramming tasks. The definition of the classType is:
class Type(fix cls: Class, crd) extends Entity {
fix card: Int = crd match {
case c if Int => c
case s if StringSymbol and s == '* => -1
case _ =>
error(“Invalid cardinality value {crd} in type declaration”())
}
}
In metaprogramming, types are used to define other entities, e.g. for setting the function/field domains and ranges.
Packages
The programmer can work with packages as entities, create and delete them. The examples of methods are:getPackagereturns a package with a certain name (or the current package)newPackagecreates a new packagepackagereturns the package, in which a given entity is definedpackagesreturns all loaded packages.
A Metaprogramming Example: Operator enum
Let us define the operationenum, which converts a sequence of string symbols into an enumeration type. For instance, the execution of the query
'WeekDay enum ('Mon, 'Tue, 'Wed, 'Thu, 'Fri, 'Sat, 'Sun)
must be equivalent to the following code:
class WeekDay object Mon extends WeekDay object Tue extends WeekDay object Wed extends WeekDay object Thu extends WeekDay object Fri extends WeekDay object Sat extends WeekDay object Sun extends WeekDay fix %WeekDay values = (Mon, Tue, Wed, Thu, Fri, Sat, Sun)Note that the external field
values is defined on the entity %WeekDay, an object of the class Class.
Now, define:
def (enumer: StringSymbol) enum (els: StringSymbol*) {
try {
fix cls = newClass(enumer) // enum class
// create and add elements of enum
fix enums = els.newObject.addObjectClass(cls)
// create field ‘values’
fix values = cls.addFixField(“values”)
cls.addValues(values, enums)
}
catch {case _ => error("failed to create enum {enumer}"!)}
}
Let us verify:
'WeekDay enum ('Mon, 'Tue, 'Wed, 'Thu, 'Fri, 'Sat, 'Sun)
%WeekDay.values // Mon Tue Wed Thu Fri Sat Sun
def WeekDay isWorkingDay = (not this in (Sat,Sun))
fix class Event(day: WeekDay, comment: String)
def Event isWorkingDayEvent = day.isWorkingDay
Event(Mon, “Meeting”).isWorkingDayEvent // true
No comments:
Post a Comment