-
Notifications
You must be signed in to change notification settings - Fork 96
Reflective layer
Mozart 2 comes with what we called a reflective layer, which enables to extend the system in Oz. It was initially designed to support the implementation of the Distributed Subsystem (DSS) entirely in Oz, but it can be used for other purposes.
This page gives a documentation of the reflective layer.
The API of the reflective layer is currently available through the boot module x-oz://boot/Reflection
. This module defines the following procedures:
{Reflection.newReflectiveEntity ?Stream ?Entity}
Creates a reflective entity Entity
associated with the stream Stream
. See below for an explanation of what is a reflective entity.
{Reflection.newReflectiveVariable ?Stream ?Variable}
Creates a reflective variable Variable
associated with the stream Stream
. See below for an explanation of what is a reflective variable.
{Reflection.bindReflectiveVariable Variable Value}
Effectively binds the reflective variable Variable
to the value Value
. See below.
{Reflection.getStructuralBehavior X ?Y}
Binds Y
to an atom representing the structural behavior of the entity X
. The possible values are:
-
value
:X
is a value. Values are compared by value, and have an atomic structure from unification's point of view. Examples: integers, booleans, atoms. -
structural
:X
is a structure. Structures are compared by value, but are not atomic from unification's point of view. Examples: records, tuples, conses. -
token
:X
is a determined entity with token behavior. Tokens are compared by reference. Examples: cells, names, objects. -
variable
:X
is a variable, or more generally a transient. Most operations wait on transients. Transients are meant to become something else later through binding. Examples: variables, read-only's, failed values.
{Reflection.become X Y}
Make the token- or variable-behaved entity X
become Y
. Afterwards, X
and Y
are indistinguishable. They became the same node in memory. The entity previously held by X
is discarded, and only Y
remains. Any other reference to X
in the system is "redirected" to Y
.
X
must not have value or structural behavior for this operation to succeed (because it plainly does not make sense). Note that Y
may have any behavior.
Example:
local X Y in
X = {NewCell 5}
Y = {NewName}
{Show @X} % 5
{Show X == Y} % false
{Reflection.become X Y}
{Show X == Y} % true
{Show @X} % type error! X is a name
end
A reflective entity created by Reflection.newReflectiveEntity
is an Oz entity that forwards any low level operation (aka, builtin) on it into a message sent on the associated stream. Let us start with an example, that you can replay in the OPI.
declare Reflection S C in
[Reflection] = {Link ['x-oz://boot/Reflection']}
{Reflection.newReflectiveEntity ?S ?C}
{Browse S}
At this point the browser displays _<future>
, which indicates that S
is a read-only variable. This is exactly the same as if we had created a port instead of a reflective entity.
Now let us apply some builtin operation on the reflective entity, like this:
{Assign C 5} {Browse completed}
We ask the browser to display completed
so that we know when the Assign
call is completed. You can observe that currently, the browser does not display completed
, but it shows that the stream has received one element:
assign(5)#_|_<future>
The primitive Assign
operation corresponds to a message assign(X)
where X
is the rhs of the assignment. The second item in the #-tuple is an acknowledgment to be filled by the reflective entity driver. Let us bind it to unit
by hand:
S.1.2 = unit
Observe that this triggers the display of completed
. This highlights the fact that Assign
was waiting for this ack to be bound before continuing.
The same is true for any primitive operation. It will send a #-tuple with two elements: a message representing the operation and its arguments, and an acknowledgment. The primitive operation waits for this acknowledgment to be bound to unit
before continuing (note: it must unit
, not another value - see later).
TODO ...