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,name
returns the name of the entityrename
renames the entitygetObject
returns 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
classFields
returns all fields associated with the classobjectsDirect
returns all instances created by the class constructorobjects
returns all instances of the classremoveSubclass
retracts the class as a subclass of another classsubclassesDirect
returns all direct subclasses of the classsubclasses
returns all subclasses of the classsubclassof
is a filter letting those entities through, which are subclasses of the classnew
is 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,
addFunction
adds an external function to a packageaddMethod
adds a method associated with a classaddFunctionDef
adds a new definition to a polymorphic functionremoveFunction
removes an external function from a packageremoveMethod
removes 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:addValues
adds values to a fielddomain
returns the domain type of a fieldmaxCard
returns the maximum allowed number of valuesaddFixField
creates a new immutable field associated with a class or an objectaddVarField
creates a new mutable field associated with a class or an objectrange
returns the range type of a field
Objects
Here are some methods working on objects.addObjectClass
adds an object to a classfields
returns the fields associated with an objectnewObject
creates a new objectowners
returns objects, a field of which has the current object as its valueremoveObjectClass
removes an object from a classremoveValue
removes a value from an object fieldremoveValues
removes all values from an object fieldsetValues
assigns new values to an object fieldvalues
returns 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:getPackage
returns a package with a certain name (or the current package)newPackage
creates a new packagepackage
returns the package, in which a given entity is definedpackages
returns 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