Starts the ground VM, in untyped and typed programs, respectively. If
that performs the
initial actions. If
is specified (with a
@@ -53,73 +53,91 @@ actions; if it is a procedure, it is called with the
produces the equivalent of
2.4 Creating endpoints
The primitive action that creates new endpoints is
add-endpoint, but because endpoints are the most flexible and
-complex point of interaction between a process and its VM, a DSL,
-endpoint, streamlines endpoint setup.
(endpoint orientation topic maybe-interest-type | maybe-let-name | maybe-name | maybe-state-pattern | maybe-on-presence | maybe-on-absence | maybe-role-patterns | maybe-reason-pattern | maybe-message-handlers) |
|
|
(endpoint: maybe-typed-state-pattern : State | orientation topic maybe-interest-type | maybe-let-name | maybe-name | maybe-on-presence | maybe-on-absence | maybe-role-patterns | maybe-reason-pattern | maybe-message-handlers) |
| | maybe-typed-state-pattern | | = | | | | | | | | pattern | | | | | | orientation | | = | | #:subscriber | | | | | | #:publisher | | | | | | topic | | = | | expr | | | | | | maybe-interest-type | | = | | | | | | | | #:participant | | | | | | #:observer | | | | | | #:everything | | | | | | maybe-let-name | | = | | | | | | | | #:let-name identifier | | | | | | maybe-name | | = | | | | | | | | #:name expr | | | | | | maybe-state-pattern | | = | | | | | | | | #:state pattern | | | | | | maybe-on-presence | | = | | | | | | | | #:on-presence handler-expr | | | | | | maybe-on-absence | | = | | | | | | | | #:on-absence handler-expr | | | | | | maybe-role-patterns | | = | | | | | | | | #:role pattern | | | | | | #:peer-orientation pattern | #:conversation pattern | #:peer-interest-type pattern |
| | | | | | maybe-reason-pattern | | = | | | | | | | | #:reason pattern | | | | | | maybe-message-handlers | | = | | | | | | | | message-handler ... | | | | | | message-handler | | = | | [pattern handler-expr] | | | | | | handler-expr | | = | | expr |
|
|
Almost everything is optional in an
endpoint. The only
-mandatory parts are the orientation and the topic. For
-
endpoint:, the expected type of the process state must also
-be supplied.
For example, a minimal endpoint subscribing to all messages would be:
(endpoint #:subscriber ?)
or in Typed Racket, for a process with Integer as its process
-state type,
(endpoint: : Integer #:subscriber ?)
A minimal publishing endpoint would be:
While topic patterns are ordinary Racket data with embedded ?
+complex point of interaction between a process and its VM, a
+collection of macros helps streamline endpoint setup.
Almost everything is optional in an endpoint definition. The only
+mandatory part is the topic, unless you’re using Typed Racket, in
+which case the process state type must also be specified.
For example, a minimal endpoint subscribing to all messages would be:
(subscriber ?)
or in Typed Racket, for a process with Integer as its process
+state type,
(subscriber: Integer ?)
A minimal publishing endpoint would be:
While topic patterns are ordinary Racket data with embedded ?
wildcards (see Messages and Topics), all the other patterns
-in an endpoint are match-patterns. In particular
+in an endpoint definition are match-patterns. In particular
note that ? is a wildcard in a topic pattern, while
-_ is a wildcard in a match-pattern.
2.4.1 Receiving messages
Supply one or more message-handler clauses to handle incoming
-message events (as distinct from presence- or absence-events).
The following endpoint subscribes to all messages, but only
-handles some of them:
2.4.2 Action-only vs. State updates
If #:state occurs in an endpoint, or the
-maybe-typed-state-pattern occurs in an endpoint:,
-then all the handler-exprs in that endpoint are expected to
-return transition structures.
If not, however, the event handler expressions are expected to return
-plain ActionTrees.
This way, simple endpoints that do not need to examine the process
+_ is a wildcard in a match-pattern.
2.4.1 Receiving messages
Supply an
on-message handler clause to an endpoint definition
+to handle incoming message events (as distinct from presence- or
+absence-events).
The following endpoint subscribes to all messages, but only
+handles some of them:
2.4.2 Action-only vs. State updates
If not, however, the handler expressions are expected to return plain
+ActionTrees.
This way, simple handlers that do not need to examine the process
state, and simply act in response to whichever event triggered them,
can be written without the clutter of threading the process state
-value through the code.
For example, a simple endpoint could be written either as
or, explicitly accessing the endpoint’s process’s state,
2.4.3 Naming endpoints
Endpoint names can be used to update
-or delete endpoints.
If #:name is supplied, the given value is used as the name of
-the endpoint. If not, a fresh name is created. (At present,
-gensym is used.)
If #:let-name is supplied, the given identifier is bound in
-the handler-exprs to the name of the endpoint. If not, the
-name of the endpoint is inaccessible to the handler-exprs.
2.4.4 Handling presence and absence events
Other endpoints (in this or other processes) may have matching topics
+value through the code.
For example, a simple endpoint could be written either as
or, explicitly accessing the endpoint’s process’s state,
2.4.3 Handling presence and absence events
Other endpoints (in this or other processes) may have matching topics
and complementary orientations to the current endpoint. When such
endpoints come and go, presence and absence events are generated in
-the current endpoint.
By default, no actions are taken on such events, but
-#:on-presence and #:on-absence override this
-behaviour.
For example, say process A establishes the following endpoint:
Some time later, process B takes the following endpoint-establishing
-action:
The sequence of events will be:
Process A’s #:on-presence handler will run, and the
+the current endpoint.
By default, no actions are taken on such events, but
+on-presence and on-absence handlers override this
+behaviour.
For example, say process A establishes the following endpoint:
Some time later, process B takes the following endpoint-establishing
+action:
The sequence of events will be:
Process A’s on-presence handler will run, and the
'pinger-arrived message will be sent. At the same
time,In the current implementation, one happens before the
other, but it is nondeterministic which is run first. process B’s
-#:on-presence handler runs, installing a second endpoint
+on-presence handler runs, installing a second endpoint
and sending the 'ping message.
Process A’s endpoint receives the 'ping message, and
sends the 'pong message.
Process B’s second endpoint receives the 'pong
-message, and deletes both of process B’s endpoints.
The #:on-absence handler in process A runs, sending
+message, and deletes both of process B’s endpoints.
The on-absence handler in process A runs, sending
the 'pinger-departed message.
One possible trace of messages in the VM containing processes A and B is
'pinger-arrived |
'ping |
'pong |
'pinger-departed |
By sending the 'ping message only once the
-#:on-presence handler has fired, process B ensures that
+on-presence handler has fired, process B ensures that
someone is listening for pings.
This way, if process B starts before process A, then B will
automatically wait until A is ready to receive ping requests before
-issuing any.
2.4.5 Exit reasons
If a #:reason pattern is supplied, then the exit reason
-supplied to the delete-endpoint or quit action that
-led to the absence-event is available to the endpoint’s
-#:on-absence handler expression.
2.4.6 Updating endpoints
If, when an endpoint is created, an existing endpoint with an
+issuing any.
2.4.4 Exit reasons
2.4.5 Updating endpoints
If, when an endpoint is created, an existing endpoint with an
equal? name is already present, then if the existing and
to-be-added endpoints have exactly equal roles (meaning equal
orientations, interest-types, and topic patterns), the handlers
for the endpoint are updated without emitting presence or
absence notifications.
This dubious feature can be used to avoid "glitching" of presence
signals. A future release of this library will include better
-automatic support for avoiding such transients.
2.4.7 Who am I talking to?
If either #:role or any of #:peer-orientation,
-#:conversation, or #:peer-interest-type are
-supplied, the handler-exprs are given access to the role
-carried in the EndpointEvent that triggered them.
This role describes the intersection of interests between the
-current endpoint and the peer endpoint, and so can proxy for the
-identity of the other party. It is in a sense a description of the
-scope of the current conversation.
Using #:role allows a handler complete access to the
-role structure in the triggering event. It is more common
-however to simply use #:conversation to extract the
-role-topic alone, since it is seldom necessary to examine
+automatic support for avoiding such transients.
2.4.6 Who am I talking to?
The carried role describes the intersection of interests
+between the current endpoint and the peer endpoint, and so can proxy
+for the identity of the other party. It is in a sense a description of
+the scope of the current conversation.
It is most common to simply use match-conversation to extract
+the role-topic alone, since it is seldom necessary to examine
role-orientation (since it’s guaranteed to be complementary
to the orientation of the current endpoint) or
-role-interest-type. If access to those parts is required, use
-#:peer-orientation and #:peer-interest-type.
2.5 Deleting endpoints
Use this action to delete a previously-added endpoint by name. The
+
role-interest-type.
See Examples for examples of the use of
+match-conversation and friends.
2.4.7 Participating in a conversation vs. observing conversations
The core build-endpoint form takes an expression evaluating
+to a role, rather than a simple topic. This gives full
+control over the new endpoint’s Orientation and
+InterestType.
The other forms exist for convenience, since usually the orientation
+and interest-type is known statically, and only the topic varies
+dynamically:
The publisher, observe-subscribers and
+observe-subscribers/everything forms create
+publisher-oriented endpoints, and subscriber,
+observe-publishers and observe-publishers/everything
+create subscriber-oriented endpoints. The rationale for this is
+that as a participant, the code should declare the role being played;
+but as an observer, the code should declare the roles being observed.
2.4.8 Naming endpoints
Endpoint names can be used to update
+or delete endpoints.
Returns a copy of the passed-in
add-endpoint action
+structure, with the
id field set to the passed-in identifying
+value.
Binds the
identifiers to freshly-gensymmed symbols so that
+they are available to the
exprs.
let-fresh is useful
+for inventing a guaranteed-unused name for a temporary endpoint:
2.5 Deleting endpoints
If reason is supplied, it is included in the corresponding
@@ -127,45 +145,48 @@ action, and made available in any resulting 'publisher; it
means that the sender of the message is acting in the "publisher"
role. Use 'subscriber instead when acting in the "subscriber"
-role, i.e. sending feedback.
2.7 Creating processes
(spawn maybe-pid-binding maybe-debug-name maybe-parent-continuation | #:child boot-expr) |
|
|
(spawn: maybe-pid-binding maybe-debug-name typed-parent-continuation | #:child : ChildStateType boot-expr) |
| | maybe-pid-binding | | = | | | | | | | | #:pid identifier | | | | | | maybe-debug-name | | = | | | | | | | | #:debug-name expr | | | | | | maybe-parent-continuation | | = | | | | | | | | #:parent k-expr | | | | | | #:parent parent-state-pattern k-expr | | | | | | typed-parent-continuation | | = | | #:parent : ParentStateType | | | | | | #:parent : ParentStateType k-expr | | | | | | #:parent parent-state-pattern : ParentStateType k-expr | | | | | | k-expr | | = | | expr | | | | | | boot-expr | | = | | expr |
|
|
Action describing a new process to create. The boot-expr
+role, i.e. sending feedback.
2.7 Creating processes
(spawn maybe-pid-binding boot-expr)
|
|
(spawn/continue maybe-pid-binding | #:parent parent-state-pattern k-expr | #:child boot-expr) |
|
|
(spawn: maybe-pid-binding | #:parent : ParentStateType | #:child : ChildStateType boot-expr) |
|
|
(spawn/continue: maybe-pid-binding | #:parent parent-state-pattern : ParentStateType k-expr | #:child : ChildStateType boot-expr) |
| | maybe-pid-binding | | = | | | | | | | | #:pid identifier | | | | | | k-expr | | = | | expr | | | | | | boot-expr | | = | | expr |
|
|
Action describing a new process to create. The
boot-expr
should be an expression yielding a
transition that contains
the child process’s initial state and initial actions.
If #:pid is supplied, the associated identifier is bound to
the child process’s PID in both boot-expr and the parent’s
-k-expr.
Any supplied #:debug-name will be used in VM debug output.
-See also logging (MARKETPLACE_LOG).
If #:parent is supplied, the associated k-expr will
-run in the parent process after the child process has been created. If
-the parent-state-pattern is also supplied, then
-k-expr must return a Transition; otherwise, it must
-return an ActionTree. Note that in Typed Racket, for type
-system reasons, spawn: requires ParentStateType to
-be supplied.
2.8 Exiting and killing processes
Action causing the termination of a process. If
who is
+
k-expr.
The spawn/continue and spawn/continue: variations
+include a k-expr, which will run in the parent process after
+the child process has been created. Note that k-expr must
+return a Transition, since parent-state-pattern is
+always supplied for these variations.
In Typed Racket, for type system reasons, spawn: and
+spawn/continue: require ParentStateType to be
+supplied as well as ChildStateType.
Returns a copy of the passed-in
spawn action structure, with
+the
debug-name field set to the passed-in identifying value.
+The debug name of a process is used in VM debug output. See also
+
logging (MARKETPLACE_LOG).
2.8 Exiting and killing processes
Action causing the termination of a process. If who is
omitted or #f, terminates the acting process; otherwise,
terminates the peer process having who as its PID.
If reason is supplied, it is included in the corresponding
action, and made available in any resulting absence-events.
Terminating the current process is as simple as:
(quit)
When a process raises an exception that it does not catch, its
containing VM catches the exception and turns it into an implicit quit
action. In that case, the reason will be the raised exception
-itself.
2.9 Cooperative scheduling
(yield maybe-state-pattern k-expr)
|
|
(yield: typed-state-pattern k-expr)
| | maybe-state-pattern | | = | | | | | | | | #:state pattern | | | | | | typed-state-pattern | | = | | : State | | | | | | pattern : State | | | | | | k-expr | | = | | expr |
|
|
Lets other processes in the system run for a step, returning to
+itself.
2.9 Cooperative scheduling
(yield state-pattern k-expr)
|
|
(yield: state-pattern : State k-expr)
|
|
Lets other processes in the system run for a step, returning to
evaluate k-expr only after doing a complete round of the
-scheduler.
If pattern is supplied, k-expr should evaluate to a
-Transition; otherwise it should produce an ActionTree.
2.10 Creating nested VMs
(nested-vm maybe-vm-pid-binding maybe-boot-pid-binding | maybe-initial-state | maybe-debug-name | boot-action-expr ...) |
|
|
(nested-vm: : ParentStateType | maybe-vm-pid-binding maybe-boot-pid-binding | maybe-typed-initial-state | maybe-debug-name | boot-action-expr ...) |
| | maybe-vm-pid-binding | | = | | | | | | | | #:vm-pid identifier | | | | | | maybe-boot-pid-binding | | = | | | | | | | | #:boot-pid identifier | | | | | | maybe-initial-state | | = | | | | | | | | #:initial-state expr | | | | | | maybe-typed-initial-state | | = | | | | | | | | #:initial-state expr : StateType | | | | | | maybe-debug-name | | = | | | | | | | | #:debug-name expr | | | | | | boot-action-expr | | = | | expr |
|
|
Results in a
spawn action that starts a nested VM. The
+scheduler.
The state of the yielding process will be matched against
+state-pattern when the process is resumed, and
+k-expr must evaluate to a Transition.
2.10 Creating nested VMs
(spawn-vm maybe-vm-pid-binding maybe-boot-pid-binding | maybe-initial-state | maybe-debug-name | boot-action-expr ...) |
|
|
(spawn-vm: : ParentStateType | maybe-vm-pid-binding maybe-boot-pid-binding | maybe-typed-initial-state | maybe-debug-name | boot-action-expr ...) |
| | maybe-vm-pid-binding | | = | | | | | | | | #:vm-pid identifier | | | | | | maybe-boot-pid-binding | | = | | | | | | | | #:boot-pid identifier | | | | | | maybe-initial-state | | = | | | | | | | | #:initial-state expr | | | | | | maybe-typed-initial-state | | = | | | | | | | | #:initial-state expr : StateType | | | | | | maybe-debug-name | | = | | | | | | | | #:debug-name expr | | | | | | boot-action-expr | | = | | expr |
|
|
Results in a
spawn action that starts a nested VM. The
primordial process in the new VM executes the boot-actions with the
given initial state. (If no initial state is supplied,
(void)
is used.)
If #:vm-pid is present, the corresponding identifier is bound
in the boot-action expressions to the container-relative PID of the
new VM itself. If #:boot-pid is present, however, the
corresponding identifier is bound to the new-VM-relative PID of the
-primordial process in the new VM.
2.11 Relaying across layers
Each VM gives its processes access to two distinct IPC facilities: the
+primordial process in the new VM.
2.11 Relaying across layers
Each VM gives its processes access to two distinct IPC facilities: the
internal one, provided for the VM’s processes to talk amongst
themselves, and the external one, the network that the VM
itself is a process within.
Marketplace’s actions can apply to either of those two networks. By
default, actions apply to the VM of the acting process directly, but
-using at-meta-level (or at-meta-level: in typed
+using at-meta-level (or at-meta-level: in typed
code) to wrap an action level-shifts the action to make it
-apply at the level of the acting process’s VM’s container instead.
For example, wrapping an endpoint in at-meta-level
+apply at the level of the acting process’s VM’s container instead.
For example, wrapping an endpoint in at-meta-level
adds a subscription to the VM’s container’s network. Instead of
listening to sibling processes of the acting process, the new endpoint
will listen to sibling processes of the acting process’s VM. In this
-example, the primordial process in the nested-vm creates an
-endpoint in the VM’s own network, the ground VM:
In this example, a new process is spawned as a sibling of the
-nested-vm rather than as a sibling of its primordial process:
Compare to this example, which spawns a sibling of the
-nested-vm’s primordial process:
\ No newline at end of file
+example, the primordial process in the nested VM creates an
+endpoint in the VM’s own network, the ground VM:
In this example, a new process is spawned as a sibling of the
+nested VM rather than as a sibling of its primordial process:
Compare to this example, which spawns a sibling of the
+nested VM’s primordial process: