|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
MirrorRoot
This is the interface of the root node of the intercessive mirrors delegation hierarchy.
Intercessive mirrors are always tied to a particular 'base' object. The default intercessive mirror is named 'defaultMirror' and is an object that understands all meta-level operations applicable on objects, implementing them using default semantics. It can be thought of as being defined as follows:
def mirrorroot := object: { def base := object: { nil } mirroredBy: self // base of the mirror root is an empty mirage def init(b) { base := b } def invoke(@args) {} def select(@args) { } ... } taggedAs: [ Mirror ]
This object can then simply be extended / composed by other objects to deviate from the default semantics. Note that the default semantics is applied to 'base' and *not* 'self.base', in other words: although child mirrors can define their own 'base' field, it is not taken into consideration by the mirror root. This also ensures that the mirror root is not abused to enact upon a mirage for which it was not assigned to be the mirror.
Hence, 'mirrors' are simply objects with the same interface as this mirrorroot object: they should be able to respond to all meta-level messages and have a 'base' field.
Method Summary | |
---|---|
Nil |
addField(Field field)
This structural meta-level operation adds a field object to the receiver mirror's base object. |
Nil |
addMethod(Method method)
This structural meta-level operation adds a method to the receiver mirror's base object. |
Nil |
addSlot(Method slot)
This structural meta-level operation adds a slot object to the receiver mirror's base object. |
at.objects.natives.NATText |
asCode()
This behavioural meta-level operation reifies the act of representing the base object as self-containing source code. |
at.objects.mirrors.NATMirage |
base()
The read-only field containing the mirror's base-level mirage. |
Object |
clone()
This meta-level operation reifies the act of cloning the base-level object. |
Nil |
defineField(Symbol name,
Object value)
This meta-level operation reifies field definition. |
Closure |
doesNotUnderstand(Symbol selector)
This behavioural meta-level operation reifies a failed dynamic method or field lookup. |
Object |
eval(Context ctx)
This behavioural meta-level operation reifies the evaluation of abstract grammar objects into values. |
Field |
grabField(Symbol selector)
This structural meta-level operation allows the metaprogrammer to reify a field of the receiver mirror's base object. |
Method |
grabMethod(Symbol selector)
This structural meta-level operation allows the metaprogrammer to reify a method defined on the receiver mirror's base object. |
Method |
grabSlot(Symbol selector)
This structural meta-level operation allows the metaprogrammer to reify a slot of the receiver mirror's base object. |
Object |
invoke(Object delegate,
MethodInvocation invocation)
This meta-level operation reifies synchronous message sending ("method invocation"). |
Object |
invokeField(Object receiver,
Symbol selector)
This meta-level operation reifies "field selection". |
Boolean |
isCloneOf(Object other)
This meta-level operation determines whether this mirror's base object is a clone of the parameter object. |
Boolean |
isExtensionOfParent()
This structural meta-level operation returns whether or not the receiver mirror's base object is an extension of its parent object. |
Boolean |
isRelatedTo(Object object)
This meta-level operation determines whether this mirror's base object is related to the parameter object by a combination of cloning and extension operators. |
Boolean |
isTaggedAs(TypeTag type)
Tests whether the receiver mirror's base object is tagged as a particular type. |
Table |
listFields()
This structural meta-level operation allows access to all of the fields defined on the receiver mirror's base object. |
Table |
listMethods()
This structural meta-level operation allows access to all of the methods defined on the receiver mirror's base object. |
Table |
listSlots()
This structural meta-level operation allows access to all of the slots defined on the receiver mirror's base object. |
Object |
newInstance(Table initargs)
This meta-level operation reifies instance creation. |
Object |
pass()
This behavioural meta-level operation reifies object serialization. |
at.objects.natives.NATText |
print()
This behavioural meta-level operation reifies the act of printing the base object in the read-eval-print loop. |
Object |
quote(Context ctx)
This behavioural meta-level operation reifies the quotation of abstract grammar elements. |
Object |
receive(AsyncMessage message)
This behavioural meta-level operation reifies the act of receiving an asynchronous message. |
Object |
removeSlot(Symbol selector)
This structural meta-level operation removes a slot from the object. |
Object |
resolve()
This behavioural meta-level operation reifies object deserialization. |
Boolean |
respondsTo(Symbol selector)
This meta-level method is used to determine whether an object has a field or method corresponding to the given selector, without actually invoking or selecting any value associated with that selector. |
Closure |
select(Object receiver,
Symbol selector)
This meta-level operation reifies first-class field or method selection. |
Object |
send(Object receiver,
AsyncMessage message)
This behavioural meta-level operation reifies the act of sending an asynchronous message. |
Table |
typeTags()
Returns all of the local type tags of this object. |
Methods inherited from interface edu.vub.at.objects.Object |
---|
super |
Method Detail |
---|
at.objects.mirrors.NATMirage base()
Object send(Object receiver, AsyncMessage message)
rcv<-m()
is
evaluated in the context of an object o, an asynchronous message
<-m()
is first created by the current actor mirror.
Subsequently, this message needs to be sent to the receiver. This
meta-level operation is reified by this method, as if by invoking:
(reflect: o).send(message)The default behaviour is to access the current actor's mirror and to ask the actor to send the message in this object's stead by invoking
actor.send(message)
receiver
- the object designated to receive the asynchronous messagemessage
- the asynchronous message to be sent by this object
Object receive(AsyncMessage message)
The default behaviour of a mirror on a local reference in response to the reception of an async message is to schedule this message for execution in a later turn in its owner's message queue. The actor will then later process the message by invoking
msg.process(self)In turn, the default message processing behaviour is to invoke the method corresponding to the message's selector on this object. Hence, usually a receive operation is translated into a invoke operation in a later turn. The reason for having a separate receive operation is that this enables the AmbientTalk meta-level programmer to distinguish between synchronously and asynchronously received messages. Far references react to receive by transmitting their message to their remote target.
message
- the message that was asynchronously sent to this object
Object invoke(Object delegate, MethodInvocation invocation)
o.m()
is:
(reflect: o).invoke(o,`m,[]). Method invocation comprises selector lookup and the application of the value bound to the selector. Selector lookup first queries an object's local fields, then the method dictionary:
Note also that the first argument to invoke denotes the so-called "receiver" of the invocation. It is this object to which the self pseudo-variable should be bound during method execution.
delegate
- the object to which self is bound during execution
of the methodinvocation
- an object encapsulating at least the invocation's
selector (a ATSymbol
) and arguments (a ATTable
).
for what happens if the selector
is not found.
Object invokeField(Object receiver, Symbol selector)
o.m
is interpreted at the meta-level as:
(reflect: o).invokeField(o, `m)
This meta-level operation is nearly identical to base_invoke(ATObject, ATMethodInvocation)
with one
important difference. When the selector is bound to a field storing a closure, this meta-level operation
does not auto-apply the closure, but returns the closure instead.
For all other cases, the following equality holds:
o.m == o.m()
or, at the meta-level:
(reflect: o).invokeField(o, `m) == (reflect: o).invoke(o, MethodInvocation.new(`m, []))
This effectively means that for client objects, it should not matter whether
a property is implemented as a field or as a pair of accessor/mutator methods.
receiver
- the base-level object from which the 'field' should be selected.selector
- a symbol denoting the name of the method, accessor or mutator to be invoked
Boolean respondsTo(Symbol selector)
The lookup process is the same as that for the invoke operation (i.e. not only the object's own fields and methods are searched, but also those of its dynamic parents).
selector
- a symbol denoting the name of a field (accessor or mutator) or method
Closure doesNotUnderstand(Symbol selector)
SelectorNotFound
exception, the mirror of the original receiver
of the method invocation or field selection is asked to handle failed lookup.
The default behaviour of doesNotUnderstand is to raise an
SelectorNotFound
exception.
This method is very reminiscent of Smalltalk's well-known doesNotUnderstand: and of Ruby's method_missing methods. There are, however, two important differences:
selector
- a symbol denoting the name of a method or field that could not be found
edu.vub.at.exceptions.XSelectorNotFound
- the default reaction to a failed selectionObject pass()
This operation allows objects to specify themselves how they should be parameter-passed during inter-actor communication. The interpreter will never pass an object to another actor directly, but instead always parameter-passes the return value of invoing pass() on the object's mirror.
Mirrors on by-copy objects implement pass as follows:
def pass() { base }Mirrors on by-reference objects implement pass by returning a far reference to their base-level object.
Object resolve()
This meta-level operation gives objects a chance to tell the interpreter which object they actually represent, because the object retained after parameter passing is the return value of the resolve operation.
Mirrors on by-copy objects, like isolates, implement resolve as follows:
def resolve() { base }In other words, by-copy objects represent themselves. By-reference objects are paremeter passed as far references. Mirrors on far references implement resolve by trying to resolve the far reference into a local, regular object reference (which is possible if the object they point to is located in the actor in which they just arrived). If it is not possible to resolve a far reference into a local object, the far reference remains a far reference.
Note that for isolates, this operation also ensures that the isolate's lexical scope is rebound to the lexical root of the recipient actor.
edu.vub.at.exceptions.XObjectOffline
- if a far reference to a local object can no longer be resolved
because the object has been taken offlineClosure select(Object receiver, Symbol selector)
o.&x
is interpreted at the meta-level as:
(reflect: o).select(o, `x)The selector lookup follows the same search rules as those for invoke. That is: first an object's local fields and method dictionary are searched, and only then the object's dynamic parent.
The select operation can be used to both select fields or methods from an object. When the selector is bound to a method, the return value of select is a closure that wraps the found method in the object in which the method was found. This ensures that the method retains its context information, such as the lexical scope in which it was defined and the value of self, which will be bound to the original receiver, i.e. the first argument of select.
If the selector matches a field, an accessor is returned. If the selector ends with :=, a mutator is returned instead. An accessor is a nullary closure which upon application yields the field's value. A mutator is a unary closure which upon application assigns the field to the specified value. Even for fields already bound to a closure, selecting the field returns an accessor closure, not the bound closure itself.
receiver
- the dynamic receiver of the selection. If the result of the selection is
a method, the closure wrapping the method will bind self to this object.selector
- a symbol denoting the name of the field or method to select.
for what happens if the selector is not found.
Nil defineField(Symbol name, Object value)
def x := v
evaluated in a lexical scope lex
is interpreted at the meta-level as:
(reflect: lex).defineField(`x, v)Invoking this meta-level operation on an object's mirror adds a new field to that object. An object cannot contain two or more fields with the same name.
name
- a symbol denoting the name of the new fieldvalue
- the value of the new field
edu.vub.at.exceptions.XDuplicateSlot
- if the object already has a
local field with the given nameObject clone()
clone: o
is interpreted at the meta-level as
(reflect: o).clone()AmbientTalk's default cloning semantics are based on shallow copying. A cloned object has copies of the original object's fields, but the values of the fields are shared between the clones. A clone has the same methods as the original object. Methods added at a later stage to the original will not affect the clone's methods and vice versa. This means that each objects has its own independent fields and methods.
If the cloned AmbientTalk object contains programmer-defined field objects, each of these fields is re-instantiated with the clone as a parameter. The clone is intialized with the re-instantiated fields rather than with the fields of the original object. This property helps to ensure that each object has its own independent fields.
If the object has a shares-a relationship with its parent, the object and its clone will share the same parent object. Shares-a relationships are the default in AmbientTalk, and they match with the semantics of shallow copying: the dynamic parent of an object is a regular field, hence its contents is shallow-copied.
If the object has an is-a relationship with its parent object, a clone of the object will receive a clone of the parent object as its parent. Hence, is-a relationships "override" the default shallow copying semantics and recursively clone the parent of an object up to a shares-a relationship.
If a mirage is cloned, its mirror is automatically re-instantiated with the new mirage, to ensure that each mirage has its independent mirror.
Object newInstance(Table initargs)
def new(@initargs) { (reflect: self).newInstance(initargs) }Creating a new instance of an object is a combination of:
def newInstance(initargs) { def instance := self.clone(); instance.init(@initargs); instance; }Instance creation in AmbientTalk is designed to mimick class instantiation in a class-based language. Instantiating a class c requires allocating a new instance i and then invoking the constructor on that new instance. In AmbientTalk, class allocation is replaced by object cloning. The benefit is that an instantiated object its variables are already initialized to useful values, being those of the object from which it is instantiated. The init method plays the role of "constructor" in AmbientTalk.
initargs
- a table denoting the actual arguments to be passed to
the init method
Nil addField(Field field)
As an example, here is how to add a read-only field foo initialized to 5 to an object obj:
def makeConstantField(nam, val) { object: { def new(newHost) { self }; // singleton pattern def name := nam; def readField() { val }; def writeField(newVal) { nil }; } }; (reflect: obj).addField(makeConstantField(`foo, 5));
field
- the prototype field object whose instance should be added
to the receiver's base object
edu.vub.at.exceptions.XDuplicateSlot
- if the base object already has a field with the
same name as the new fieldNil addMethod(Method method)
method
- a method object to add to the receiver's base object's
method dictionary.
edu.vub.at.exceptions.XDuplicateSlot
- if a method with the new method's selector already
exists in the base object.Field grabField(Symbol selector)
obj.super := val
at the meta-level as:
def superField := (reflect: obj).grabField(`super); superField.writeField(val);Another important difference between select, lookup and grabField is that grabField only considers the fields local to the receiver's base object. Fields of lexical or dynamic parent objects are not considered.
selector
- a symbol representing the name of the field to select.
edu.vub.at.exceptions.XUndefinedSlot
- if the field cannot be found within the receiver's
base object.Method grabMethod(Symbol selector)
Also, unlike select and lookup, grabField only considers the locally defined methods of an object, methods of lexical or dynamic parent objects are not considered.
selector
- a symbol representing the name of the method to grab from
the receiver's base object.
edu.vub.at.exceptions.XSelectorNotFound
- if the method object cannot be found within the
receiver's base object.Table listFields()
Field
).for details about the returned
field objects.
Table listMethods()
Method
).for details about the returned
method objects.
Nil addSlot(Method slot)
As an example, here is how to add a read-only field foo initialized to 5 to an object obj:
def [accessor,mutator] := /.at.lang.values.createFieldSlot(`foo, 5); (reflect: obj).addSlot(accessor);
slot
- the method representing the slot to be added
to the receiver's base object
edu.vub.at.exceptions.XDuplicateSlot
- if the base object already has a slot with the
same name as the new slotMethod grabSlot(Symbol selector)
obj.super := val
at the meta-level as:
def superMutator := (reflect: obj).grabSlot(`super:=); superMutator(val);Another important difference between select, lookup and grabSlot is that grabSlot only considers the slots local to the receiver's base object. Slots of lexical or dynamic parent objects are not considered.
selector
- a symbol representing the name of the slot to select.
edu.vub.at.exceptions.XUndefinedSlot
- if the field cannot be found within the receiver's
base object.Table listSlots()
Method
).for details about the returned
slot objects.
Object removeSlot(Symbol selector)
selector
- the name of the slot to remove
edu.vub.at.exceptions.XSelectorNotFound
- if no slot with the given name is found in the objectBoolean isExtensionOfParent()
In AmbientTalk, all objects are part of a dynamic parent delegation chain: each object has a super field that denotes the object to which to delegate messages the object cannot understand itself. There are, however, two kinds of delegation links:
(reflect: (extend: parent with: code)).isExtensionOfParent() => true (reflect: (share: parent with: code)).isExtensionOfParent() => falseNote that accessing the dynamic parent itself is not a meta-level operation, the dynamic parent can simply be accessed from the base level by performing
obj.super
.
Object eval(Context ctx)
x
is equivalent to
evaluating (reflect: `x).eval(ctx)
where ctx
is a reification of the current evaluation context.
ctx
- a context object that stores the current lexical scope and
the current value of self
edu.vub.at.exceptions.XIllegalUnquote
- if an unquote abstract grammar element is evaluated. Such
abstract grammar elements should only be encountered in a quoted parse tree.Object quote(Context ctx)
ctx
- a context object passed on to be used in subsequent evaluations.
edu.vub.at.exceptions.XIllegalQuote
- exception whenever an unquote-splice unquotation is discovered
in an Abstract Grammar node where the resulting table cannot be spliced.at.objects.natives.NATText print()
at.objects.natives.NATText asCode()
Boolean isRelatedTo(Object object)
def isRelatedTo(object) { self.isCloneOf(object).or: { (reflect: base.super).isRelatedTo(object) } }
object
- the object to compare this mirror's base object to
Boolean isCloneOf(Object other)
other
- the object to check the is-clone-of relationship with.
Boolean isTaggedAs(TypeTag type)
TypeTag.base_isSubtypeOf(ATTypeTag)
method. If no local type
is found, the test is applied recursively on this object's dynamic parent. In code:
def isTaggedAs(type) { (nil != (self.tagsOf: object).find: { |localType| localType.isSubtypeOf(type) }).or: { (reflect: base.super).isTaggedAs(type) } };The primitive method is: obj taggedAs: type is defined in terms of this method:
def is: obj taggedAs: type { (reflect: obj).isTaggedAs(type) };
type
- the type tag object to check for
Table typeTags()
def tagsOf: obj { (reflect: obj).typeTags };
|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |