Update documentation for extrasugar
This commit is contained in:
parent
f671ac3bef
commit
08879f2a9a
|
@ -130,14 +130,15 @@ Choose a @racket[tcp-handle], and then create endpoints as follows:
|
||||||
(let ((local (tcp-handle 'some-unique-value))
|
(let ((local (tcp-handle 'some-unique-value))
|
||||||
(remote (tcp-address "the.remote.host.example.com" 5999)))
|
(remote (tcp-address "the.remote.host.example.com" 5999)))
|
||||||
(transition/no-state
|
(transition/no-state
|
||||||
(endpoint #:publisher (tcp-channel local remote ?))
|
(publisher (tcp-channel local remote ?))
|
||||||
(endpoint #:subscriber (tcp-channel remote local ?)
|
(subscriber (tcp-channel remote local ?)
|
||||||
[(tcp-channel _ _ (? eof-object?))
|
(on-message
|
||||||
(code:comment "Handle a received end-of-file object")
|
[(tcp-channel _ _ (? eof-object?))
|
||||||
(transition ...)]
|
(code:comment "Handle a received end-of-file object")
|
||||||
[(tcp-channel _ _ (? bytes? data))
|
(transition ...)]
|
||||||
(code:comment "Handle received data")
|
[(tcp-channel _ _ (? bytes? data))
|
||||||
(transition ...)])))
|
(code:comment "Handle received data")
|
||||||
|
(transition ...)]))))
|
||||||
]
|
]
|
||||||
|
|
||||||
The TCP driver will automatically create an outbound connection in
|
The TCP driver will automatically create an outbound connection in
|
||||||
|
@ -153,18 +154,18 @@ Choose a port number, and then create an @emph{observer} endpoint as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(endpoint #:subscriber (tcp-channel ? (tcp-listener 5999) ?) #:observer
|
(observe-publishers (tcp-channel ? (tcp-listener 5999) ?)
|
||||||
#:conversation (tcp-channel them us _)
|
(match-conversation (tcp-channel them us _)
|
||||||
#:on-presence (spawn #:child (chat-session them us)))
|
(on-presence (spawn (chat-session them us)))))
|
||||||
]
|
]
|
||||||
|
|
||||||
The use of @racket[#:observer] here indicates that this endpoint isn't
|
The use of @racket[observe-publishers] here indicates that this endpoint isn't
|
||||||
actually interested in exchanging any TCP data; instead, it is
|
actually interested in exchanging any TCP data; instead, it is
|
||||||
monitoring demand for such exchanges. The TCP driver uses a rare
|
monitoring demand for such exchanges. The TCP driver uses the unusual
|
||||||
@racket[#:everything] endpoint to monitor the presence of
|
@racket['everything] @racket[InterestType] to monitor the presence of
|
||||||
@racket[#:observer]s, and creates listening TCP server sockets in
|
@racket['observer]s, and creates listening TCP server sockets in
|
||||||
response. When a connection comes in, the TCP driver spawns a manager
|
response. When a connection comes in, the TCP driver spawns a manager
|
||||||
process which offers regular @racket[#:participant] endpoints for
|
process which offers regular @racket['participant] endpoints for
|
||||||
communicating on the newly-arrived socket.
|
communicating on the newly-arrived socket.
|
||||||
|
|
||||||
To illustrate the code for handling a newly-arrived connection,
|
To illustrate the code for handling a newly-arrived connection,
|
||||||
|
@ -172,11 +173,11 @@ To illustrate the code for handling a newly-arrived connection,
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define (chat-session them us)
|
(define (chat-session them us)
|
||||||
(transition/no-state
|
(transition/no-state
|
||||||
(endpoint #:subscriber (tcp-channel them us ?)
|
(subscriber (tcp-channel them us ?)
|
||||||
#:on-absence (quit)
|
(on-absence (quit))
|
||||||
[(tcp-channel _ _ (? bytes? data))
|
(on-message [(tcp-channel _ _ (? bytes? data))
|
||||||
(code:comment "Handle incoming data")
|
(code:comment "Handle incoming data")
|
||||||
(transition ...)])))
|
(transition ...)]))))
|
||||||
]
|
]
|
||||||
|
|
||||||
@subsection{Receiving data}
|
@subsection{Receiving data}
|
||||||
|
|
|
@ -77,11 +77,10 @@ start any drivers they need; for example, the file
|
||||||
@racket[tcp] and an initial @racket[endpoint] action:
|
@racket[tcp] and an initial @racket[endpoint] action:
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(ground-vm
|
(ground-vm tcp
|
||||||
tcp
|
(subscriber (tcp-channel ? (tcp-listener 5999) ?)
|
||||||
(endpoint #:subscriber (tcp-channel ? (tcp-listener 5999) ?)
|
(match-conversation (tcp-channel from to _)
|
||||||
#:conversation (tcp-channel from to _)
|
(on-presence (spawn (echoer from to))))))
|
||||||
#:on-presence (spawn #:child (echoer from to))))
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@deftogether[(
|
@deftogether[(
|
||||||
|
@ -241,139 +240,106 @@ produces the equivalent of
|
||||||
|
|
||||||
The primitive action that creates new endpoints is
|
The primitive action that creates new endpoints is
|
||||||
@racket[add-endpoint], but because endpoints are the most flexible and
|
@racket[add-endpoint], but because endpoints are the most flexible and
|
||||||
complex point of interaction between a process and its VM, a DSL,
|
complex point of interaction between a process and its VM, a
|
||||||
@racket[endpoint], streamlines endpoint setup.
|
collection of macros helps streamline endpoint setup.
|
||||||
|
|
||||||
@deftogether[(
|
@deftogether[(
|
||||||
@defform[(endpoint orientation topic maybe-interest-type
|
@defform[(publisher topic handler ...)]
|
||||||
maybe-let-name
|
@defform[(publisher: State topic handler ...)]
|
||||||
maybe-name
|
@defform[(subscriber topic handler ...)]
|
||||||
maybe-state-pattern
|
@defform[(subscriber: State topic handler ...)]
|
||||||
maybe-on-presence
|
@defform[(observe-subscribers topic handler ...)]
|
||||||
maybe-on-absence
|
@defform[(observe-subscribers: State topic handler ...)]
|
||||||
maybe-role-patterns
|
@defform[(observe-publishers topic handler ...)]
|
||||||
maybe-reason-pattern
|
@defform[(observe-publishers: State topic handler ...)]
|
||||||
maybe-message-handlers)]
|
@defform[(observe-subscribers/everything topic handler ...)]
|
||||||
@defform[#:literals (:)
|
@defform[(observe-subscribers/everything: State topic handler ...)]
|
||||||
(endpoint: maybe-typed-state-pattern : State
|
@defform[(observe-publishers/everything topic handler ...)]
|
||||||
orientation topic maybe-interest-type
|
@defform[(observe-publishers/everything: State topic handler ...)]
|
||||||
maybe-let-name
|
@defform[(build-endpoint pre-eid role handler ...)]
|
||||||
maybe-name
|
@defform[(build-endpoint: State pre-eid role handler ...)
|
||||||
maybe-on-presence
|
|
||||||
maybe-on-absence
|
|
||||||
maybe-role-patterns
|
|
||||||
maybe-reason-pattern
|
|
||||||
maybe-message-handlers)
|
|
||||||
#:grammar
|
#:grammar
|
||||||
[(maybe-typed-state-pattern (code:line)
|
[(handler unfiltered-handler
|
||||||
(code:line pattern))
|
(match-state pattern handler ...)
|
||||||
(orientation #:subscriber
|
(match-orientation pattern handler ...)
|
||||||
#:publisher)
|
(match-conversation pattern handler ...)
|
||||||
(topic expr)
|
(match-interest-type pattern handler ...)
|
||||||
(maybe-interest-type (code:line)
|
(match-reason pattern handler ...))
|
||||||
#:participant
|
(unfiltered-handler (on-presence expr ...)
|
||||||
#:observer
|
(on-absence expr ...)
|
||||||
#:everything)
|
(on-message [pattern expr ...] ...))]]
|
||||||
(maybe-let-name (code:line)
|
|
||||||
(code:line #:let-name identifier))
|
|
||||||
(maybe-name (code:line)
|
|
||||||
(code:line #:name expr))
|
|
||||||
(maybe-state-pattern (code:line)
|
|
||||||
(code:line #:state pattern))
|
|
||||||
(maybe-on-presence (code:line)
|
|
||||||
(code:line #:on-presence handler-expr))
|
|
||||||
(maybe-on-absence (code:line)
|
|
||||||
(code:line #:on-absence handler-expr))
|
|
||||||
(maybe-role-patterns (code:line)
|
|
||||||
(code:line #:role pattern)
|
|
||||||
(code:line #:peer-orientation pattern
|
|
||||||
#:conversation pattern
|
|
||||||
#:peer-interest-type pattern))
|
|
||||||
(maybe-reason-pattern (code:line)
|
|
||||||
(code:line #:reason pattern))
|
|
||||||
(maybe-message-handlers (code:line)
|
|
||||||
(code:line message-handler ...))
|
|
||||||
(message-handler [pattern handler-expr])
|
|
||||||
(handler-expr expr)]]
|
|
||||||
)]{
|
)]{
|
||||||
|
|
||||||
Almost everything is optional in an @racket[endpoint]. The only
|
The many variations on the core
|
||||||
mandatory parts are the orientation and the topic. For
|
@racket[build-endpoint]/@racket[build-endpoint:] form exist to give
|
||||||
@racket[endpoint:], the expected type of the process state must also
|
good control over @racket[InterestType] in the endpoint under
|
||||||
be supplied.
|
construction;
|
||||||
|
see @secref{participating-vs-observing}.
|
||||||
|
|
||||||
|
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:
|
For example, a minimal endpoint subscribing to all messages would be:
|
||||||
|
|
||||||
@racketblock[(endpoint #:subscriber ?)]
|
@racketblock[(subscriber ?)]
|
||||||
|
|
||||||
or in Typed Racket, for a process with @racket[Integer] as its process
|
or in Typed Racket, for a process with @racket[Integer] as its process
|
||||||
state type,
|
state type,
|
||||||
|
|
||||||
@racketblock[(endpoint: : Integer #:subscriber ?)]
|
@racketblock[(subscriber: Integer ?)]
|
||||||
|
|
||||||
A minimal publishing endpoint would be:
|
A minimal publishing endpoint would be:
|
||||||
|
|
||||||
@racketblock[(endpoint #:publisher ?)
|
@racketblock[(publisher ?)
|
||||||
(endpoint: : Integer #:publisher ?)]
|
(publisher: Integer ?)]
|
||||||
|
|
||||||
While topic patterns are ordinary Racket data with embedded @racket[?]
|
While topic patterns are ordinary Racket data with embedded @racket[?]
|
||||||
wildcards (see @secref{messages-and-topics}), all the other patterns
|
wildcards (see @secref{messages-and-topics}), all the other patterns
|
||||||
in an @racket[endpoint] are @racket[match]-patterns. In particular
|
in an endpoint definition are @racket[match]-patterns. In particular
|
||||||
note that @racket[?] is a wildcard in a topic pattern, while
|
note that @racket[?] is a wildcard in a topic pattern, while
|
||||||
@racket[_] is a wildcard in a @racket[match]-pattern.
|
@racket[_] is a wildcard in a @racket[match]-pattern.
|
||||||
|
|
||||||
@subsection{Receiving messages}
|
@subsection{Receiving messages}
|
||||||
|
|
||||||
Supply one or more @racket[message-handler] clauses to handle incoming
|
Supply an @racket[on-message] handler clause to an endpoint definition
|
||||||
message events (as distinct from presence- or absence-events).
|
to handle incoming message events (as distinct from presence- or
|
||||||
|
absence-events).
|
||||||
|
|
||||||
The following endpoint @emph{subscribes} to all messages, but only
|
The following endpoint @emph{subscribes} to all messages, but only
|
||||||
@emph{handles} some of them:
|
@emph{handles} some of them:
|
||||||
|
|
||||||
@racketblock[(endpoint #:subscriber ?
|
@racketblock[(subscriber ?
|
||||||
['ping (send-message 'pong)]
|
(on-message
|
||||||
['hello (list (send-message 'goodbye)
|
['ping (send-message 'pong)]
|
||||||
(quit))])]
|
['hello (list (send-message 'goodbye)
|
||||||
|
(quit))]))]
|
||||||
|
|
||||||
@subsection{Action-only vs. State updates}
|
@subsection{Action-only vs. State updates}
|
||||||
|
|
||||||
If @racket[#:state] occurs in an @racket[endpoint], or the
|
If a group of handlers is wrapped in @racket[match-state], then all
|
||||||
@racket[maybe-typed-state-pattern] occurs in an @racket[endpoint:],
|
the wrapped handlers are expected to return
|
||||||
then all the @racket[handler-expr]s in that endpoint are expected to
|
@seclink["constructing-transitions"]{transition structures}.
|
||||||
return @seclink["constructing-transitions"]{transition structures}.
|
|
||||||
|
|
||||||
If not, however, the event handler expressions are expected to return
|
If not, however, the handler expressions are expected to return plain
|
||||||
plain @racket[ActionTree]s.
|
@racket[ActionTree]s.
|
||||||
|
|
||||||
This way, simple endpoints that do not need to examine the process
|
This way, simple handlers that do not need to examine the process
|
||||||
state, and simply act in response to whichever event triggered them,
|
state, and simply act in response to whichever event triggered them,
|
||||||
can be written without the clutter of threading the process state
|
can be written without the clutter of threading the process state
|
||||||
value through the code.
|
value through the code.
|
||||||
|
|
||||||
For example, a simple endpoint could be written either as
|
For example, a simple endpoint could be written either as
|
||||||
|
|
||||||
@racketblock[(endpoint #:subscriber 'ping
|
@racketblock[(subscriber 'ping
|
||||||
['ping (send-message 'pong)])]
|
(on-message ['ping (send-message 'pong)]))]
|
||||||
|
|
||||||
or, explicitly accessing the endpoint's process's state,
|
or, explicitly accessing the endpoint's process's state,
|
||||||
|
|
||||||
@racketblock[(endpoint #:subscriber 'ping
|
@racketblock[(subscriber 'ping
|
||||||
#:state old-state
|
(match-state old-state
|
||||||
['ping (transition old-state
|
(on-message ['ping (transition old-state
|
||||||
(send-message 'pong))])]
|
(send-message 'pong))])))]
|
||||||
|
|
||||||
@subsection[#:tag "naming-endpoints"]{Naming endpoints}
|
|
||||||
|
|
||||||
Endpoint names can be used to @seclink["updating-endpoints"]{update}
|
|
||||||
or @seclink["deleting-endpoints"]{delete} endpoints.
|
|
||||||
|
|
||||||
If @racket[#:name] is supplied, the given value is used as the name of
|
|
||||||
the endpoint. If not, a fresh name is created. (At present,
|
|
||||||
@racket[gensym] is used.)
|
|
||||||
|
|
||||||
If @racket[#:let-name] is supplied, the given identifier is bound in
|
|
||||||
the @racket[handler-expr]s to the name of the endpoint. If not, the
|
|
||||||
name of the endpoint is inaccessible to the @racket[handler-expr]s.
|
|
||||||
|
|
||||||
@subsection{Handling presence and absence events}
|
@subsection{Handling presence and absence events}
|
||||||
|
|
||||||
|
@ -383,37 +349,39 @@ endpoints come and go, presence and absence events are generated in
|
||||||
the current endpoint.
|
the current endpoint.
|
||||||
|
|
||||||
By default, no actions are taken on such events, but
|
By default, no actions are taken on such events, but
|
||||||
@racket[#:on-presence] and @racket[#:on-absence] override this
|
@racket[on-presence] and @racket[on-absence] handlers override this
|
||||||
behaviour.
|
behaviour.
|
||||||
|
|
||||||
For example, say process A establishes the following endpoint:
|
For example, say process A establishes the following endpoint:
|
||||||
|
|
||||||
@racketblock[(endpoint #:subscriber 'ping
|
@racketblock[(subscriber 'ping
|
||||||
#:on-presence (send-message 'pinger-arrived)
|
(on-presence (send-message 'pinger-arrived))
|
||||||
#:on-absence (send-message 'pinger-departed)
|
(on-absence (send-message 'pinger-departed))
|
||||||
['ping (send-message 'pong)])]
|
(on-message ['ping (send-message 'pong)]))]
|
||||||
|
|
||||||
Some time later, process B takes the following endpoint-establishing
|
Some time later, process B takes the following endpoint-establishing
|
||||||
action:
|
action:
|
||||||
|
|
||||||
@racketblock[(endpoint #:publisher 'ping
|
@racketblock[(let-fresh (ping-endpoint-name pong-waiter-name)
|
||||||
#:let-name ping-endpoint-name
|
(name-endpoint ping-endpoint-name
|
||||||
#:on-presence
|
(publisher 'ping
|
||||||
(list (endpoint #:subscriber 'pong
|
(on-presence
|
||||||
#:let-name pong-waiter-name
|
(list (name-endpoint pong-waiter-name
|
||||||
['pong (list (delete-endpoint ping-endpoint-name)
|
(subscriber 'pong
|
||||||
(delete-endpoint pong-waiter-name))])
|
(on-message
|
||||||
(send-message 'ping)))]
|
['pong (list (delete-endpoint ping-endpoint-name)
|
||||||
|
(delete-endpoint pong-waiter-name))])))
|
||||||
|
(send-message 'ping))))))]
|
||||||
|
|
||||||
The sequence of events will be:
|
The sequence of events will be:
|
||||||
|
|
||||||
@itemlist[
|
@itemlist[
|
||||||
|
|
||||||
@item{Process A's @racket[#:on-presence] handler will run, and the
|
@item{Process A's @racket[on-presence] handler will run, and the
|
||||||
@racket['pinger-arrived] message will be sent. At the same
|
@racket['pinger-arrived] message will be sent. At the same
|
||||||
time,@note{In the current implementation, one happens before the
|
time,@note{In the current implementation, one happens before the
|
||||||
other, but it is nondeterministic which is run first.} process B's
|
other, but it is nondeterministic which is run first.} process B's
|
||||||
@racket[#:on-presence] handler runs, installing a second endpoint
|
@racket[on-presence] handler runs, installing a second endpoint
|
||||||
and sending the @racket['ping] message.}
|
and sending the @racket['ping] message.}
|
||||||
|
|
||||||
@item{Process A's endpoint receives the @racket['ping] message, and
|
@item{Process A's endpoint receives the @racket['ping] message, and
|
||||||
|
@ -422,7 +390,7 @@ The sequence of events will be:
|
||||||
@item{Process B's second endpoint receives the @racket['pong]
|
@item{Process B's second endpoint receives the @racket['pong]
|
||||||
message, and deletes both of process B's endpoints.}
|
message, and deletes both of process B's endpoints.}
|
||||||
|
|
||||||
@item{The @racket[#:on-absence] handler in process A runs, sending
|
@item{The @racket[on-absence] handler in process A runs, sending
|
||||||
the @racket['pinger-departed] message.}
|
the @racket['pinger-departed] message.}
|
||||||
|
|
||||||
#:style 'ordered]
|
#:style 'ordered]
|
||||||
|
@ -435,7 +403,7 @@ One possible trace of messages in the VM containing processes A and B is
|
||||||
'pinger-departed]
|
'pinger-departed]
|
||||||
|
|
||||||
By sending the @racket['ping] message @emph{only} once the
|
By sending the @racket['ping] message @emph{only} once the
|
||||||
@racket[#:on-presence] handler has fired, process B ensures that
|
@racket[on-presence] handler has fired, process B ensures that
|
||||||
someone is listening for pings.
|
someone is listening for pings.
|
||||||
|
|
||||||
This way, if process B starts before process A, then B will
|
This way, if process B starts before process A, then B will
|
||||||
|
@ -444,10 +412,10 @@ issuing any.
|
||||||
|
|
||||||
@subsection{Exit reasons}
|
@subsection{Exit reasons}
|
||||||
|
|
||||||
If a @racket[#:reason] pattern is supplied, then the exit reason
|
If a handler is wrapped in a @racket[match-reason] form, then the exit
|
||||||
supplied to the @racket[delete-endpoint] or @racket[quit] action that
|
reason supplied to the @racket[delete-endpoint] or @racket[quit]
|
||||||
led to the @racket[absence-event] is available to the endpoint's
|
action that led to the @racket[absence-event] is available to the
|
||||||
@racket[#:on-absence] handler expression.
|
endpoint's @racket[on-absence] handler expression.
|
||||||
|
|
||||||
@subsection[#:tag "updating-endpoints"]{Updating endpoints}
|
@subsection[#:tag "updating-endpoints"]{Updating endpoints}
|
||||||
|
|
||||||
|
@ -464,24 +432,86 @@ automatic support for avoiding such transients.
|
||||||
|
|
||||||
@subsection{Who am I talking to?}
|
@subsection{Who am I talking to?}
|
||||||
|
|
||||||
If either @racket[#:role] or any of @racket[#:peer-orientation],
|
Wrapping a handler in @racket[match-orientation],
|
||||||
@racket[#:conversation], or @racket[#:peer-interest-type] are
|
@racket[match-conversation], and/or @racket[match-interest-type] gives
|
||||||
supplied, the @racket[handler-expr]s are given access to the role
|
a handler access to the contents of the @racket[role] structure
|
||||||
carried in the @racket[EndpointEvent] that triggered them.
|
carried in the triggering @racket[EndpointEvent].
|
||||||
|
|
||||||
This role describes the @emph{intersection of interests} between the
|
The carried role describes the @emph{intersection of interests}
|
||||||
current endpoint and the peer endpoint, and so can proxy for the
|
between the current endpoint and the peer endpoint, and so can proxy
|
||||||
identity of the other party. It is in a sense a description of the
|
for the identity of the other party. It is in a sense a description of
|
||||||
scope of the current conversation.
|
the scope of the current conversation.
|
||||||
|
|
||||||
Using @racket[#:role] allows a handler complete access to the
|
It is most common to simply use @racket[match-conversation] to extract
|
||||||
@racket[role] structure in the triggering event. It is more common
|
the @racket[role-topic] alone, since it is seldom necessary to examine
|
||||||
however to simply use @racket[#:conversation] to extract the
|
|
||||||
@racket[role-topic] alone, since it is seldom necessary to examine
|
|
||||||
@racket[role-orientation] (since it's guaranteed to be complementary
|
@racket[role-orientation] (since it's guaranteed to be complementary
|
||||||
to the orientation of the current endpoint) or
|
to the orientation of the current endpoint) or
|
||||||
@racket[role-interest-type]. If access to those parts is required, use
|
@racket[role-interest-type].
|
||||||
@racket[#:peer-orientation] and @racket[#:peer-interest-type].
|
|
||||||
|
See @secref{Examples} for examples of the use of
|
||||||
|
@racket[match-conversation] and friends.
|
||||||
|
|
||||||
|
@subsection[#:tag "participating-vs-observing"]{Participating in a conversation vs. observing conversations}
|
||||||
|
|
||||||
|
The core @racket[build-endpoint] form takes an expression evaluating
|
||||||
|
to a @racket[role], rather than a simple topic. This gives full
|
||||||
|
control over the new endpoint's @racket[Orientation] and
|
||||||
|
@racket[InterestType].
|
||||||
|
|
||||||
|
The other forms exist for convenience, since usually the orientation
|
||||||
|
and interest-type is known statically, and only the topic varies
|
||||||
|
dynamically:
|
||||||
|
|
||||||
|
@itemlist[
|
||||||
|
|
||||||
|
@item{@racket[publisher] and @racket[subscriber] (and typed
|
||||||
|
variations ending in @tt{:}) are for ordinary @emph{participation} in
|
||||||
|
conversations;}
|
||||||
|
|
||||||
|
@item{@racket[observe-subscribers] and @racket[observe-publishers]
|
||||||
|
are for @emph{observing} conversations without participating in them; and}
|
||||||
|
|
||||||
|
@item{@racket[observe-subscribers/everything] and
|
||||||
|
@racket[observe-publishers/everything] are like the ordinary
|
||||||
|
@tt{observe-...} variants, but use interest-type @racket['everything]
|
||||||
|
instead of @racket['observer].}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
The @racket[publisher], @racket[observe-subscribers] and
|
||||||
|
@racket[observe-subscribers/everything] forms create
|
||||||
|
@emph{publisher}-oriented endpoints, and @racket[subscriber],
|
||||||
|
@racket[observe-publishers] and @racket[observe-publishers/everything]
|
||||||
|
create @emph{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.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@subsection[#:tag "naming-endpoints"]{Naming endpoints}
|
||||||
|
|
||||||
|
Endpoint names can be used to @seclink["updating-endpoints"]{update}
|
||||||
|
or @seclink["deleting-endpoints"]{delete} endpoints.
|
||||||
|
|
||||||
|
@defproc[(name-endpoint [id Any] [add-endpoint-action AddEndpoint]) AddEndpoint]{
|
||||||
|
|
||||||
|
Returns a copy of the passed-in @racket[add-endpoint] action
|
||||||
|
structure, with the @racket[id] field set to the passed-in identifying
|
||||||
|
value.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@defform[(let-fresh (identifier ...) expr ...)]{
|
||||||
|
|
||||||
|
Binds the @racket[identifier]s to freshly-gensymmed symbols so that
|
||||||
|
they are available to the @racket[exprs]. @racket[let-fresh] is useful
|
||||||
|
for inventing a guaranteed-unused name for a temporary endpoint:
|
||||||
|
|
||||||
|
@racketblock[(let-fresh (my-name)
|
||||||
|
(name-endpoint my-name
|
||||||
|
(subscriber ?
|
||||||
|
(on-message [_ (list (delete-endpoint my-name)
|
||||||
|
...)]))))]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,22 +552,21 @@ Equivalent to @racket[(send-message body 'subscriber)].
|
||||||
@section{Creating processes}
|
@section{Creating processes}
|
||||||
|
|
||||||
@deftogether[(
|
@deftogether[(
|
||||||
@defform[(spawn maybe-pid-binding maybe-debug-name maybe-parent-continuation
|
@defform[(spawn maybe-pid-binding boot-expr)]
|
||||||
#:child boot-expr)]
|
@defform[(spawn/continue maybe-pid-binding
|
||||||
|
#:parent parent-state-pattern k-expr
|
||||||
|
#:child boot-expr)]
|
||||||
@defform[#:literals (:)
|
@defform[#:literals (:)
|
||||||
(spawn: maybe-pid-binding maybe-debug-name typed-parent-continuation
|
(spawn: maybe-pid-binding
|
||||||
#:child : ChildStateType boot-expr)
|
#:parent : ParentStateType
|
||||||
|
#:child : ChildStateType boot-expr)]
|
||||||
|
@defform[#:literals (:)
|
||||||
|
(spawn/continue: maybe-pid-binding
|
||||||
|
#:parent parent-state-pattern : ParentStateType k-expr
|
||||||
|
#:child : ChildStateType boot-expr)
|
||||||
#:grammar
|
#:grammar
|
||||||
[(maybe-pid-binding (code:line)
|
[(maybe-pid-binding (code:line)
|
||||||
(code:line #:pid identifier))
|
(code:line #:pid identifier))
|
||||||
(maybe-debug-name (code:line)
|
|
||||||
(code:line #:debug-name expr))
|
|
||||||
(maybe-parent-continuation (code:line)
|
|
||||||
(code:line #:parent k-expr)
|
|
||||||
(code:line #:parent parent-state-pattern k-expr))
|
|
||||||
(typed-parent-continuation (code:line #:parent : ParentStateType)
|
|
||||||
(code:line #:parent : ParentStateType k-expr)
|
|
||||||
(code:line #:parent parent-state-pattern : ParentStateType k-expr))
|
|
||||||
(k-expr expr)
|
(k-expr expr)
|
||||||
(boot-expr expr)]]
|
(boot-expr expr)]]
|
||||||
)]{
|
)]{
|
||||||
|
@ -550,16 +579,24 @@ If @racket[#:pid] is supplied, the associated identifier is bound to
|
||||||
the child process's PID in both @racket[boot-expr] and the parent's
|
the child process's PID in both @racket[boot-expr] and the parent's
|
||||||
@racket[k-expr].
|
@racket[k-expr].
|
||||||
|
|
||||||
Any supplied @racket[#:debug-name] will be used in VM debug output.
|
The @racket[spawn/continue] and @racket[spawn/continue:] variations
|
||||||
See also @secref{logging}.
|
include a @racket[k-expr], which will run in the parent process after
|
||||||
|
the child process has been created. Note that @racket[k-expr] must
|
||||||
|
return a @racket[Transition], since @racket[parent-state-pattern] is
|
||||||
|
always supplied for these variations.
|
||||||
|
|
||||||
If @racket[#:parent] is supplied, the associated @racket[k-expr] will
|
In Typed Racket, for type system reasons, @racket[spawn:] and
|
||||||
run in the parent process after the child process has been created. If
|
@racket[spawn/continue:] require @racket[ParentStateType] to be
|
||||||
the @racket[parent-state-pattern] is also supplied, then
|
supplied as well as @racket[ChildStateType].
|
||||||
@racket[k-expr] must return a @racket[Transition]; otherwise, it must
|
|
||||||
return an @racket[ActionTree]. Note that in Typed Racket, for type
|
}
|
||||||
system reasons, @racket[spawn:] requires @racket[ParentStateType] to
|
|
||||||
be supplied.
|
@defproc[(name-process [id Any] [spawn-action Spawn]) Spawn]{
|
||||||
|
|
||||||
|
Returns a copy of the passed-in @racket[spawn] action structure, with
|
||||||
|
the @racket[debug-name] field set to the passed-in identifying value.
|
||||||
|
The debug name of a process is used in VM debug output. See also
|
||||||
|
@secref{logging}.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,23 +625,18 @@ itself.
|
||||||
@section{Cooperative scheduling}
|
@section{Cooperative scheduling}
|
||||||
|
|
||||||
@deftogether[(
|
@deftogether[(
|
||||||
@defform[(yield maybe-state-pattern k-expr)]
|
@defform[(yield state-pattern k-expr)]
|
||||||
@defform[#:literals (:)
|
@defform[#:literals (:)
|
||||||
(yield: typed-state-pattern k-expr)
|
(yield: state-pattern : State k-expr)]
|
||||||
#:grammar
|
|
||||||
[(maybe-state-pattern (code:line)
|
|
||||||
(code:line #:state pattern))
|
|
||||||
(typed-state-pattern (code:line : State)
|
|
||||||
(code:line pattern : State))
|
|
||||||
(k-expr expr)]]
|
|
||||||
)]{
|
)]{
|
||||||
|
|
||||||
Lets other processes in the system run for a step, returning to
|
Lets other processes in the system run for a step, returning to
|
||||||
evaluate @racket[k-expr] only after doing a complete round of the
|
evaluate @racket[k-expr] only after doing a complete round of the
|
||||||
scheduler.
|
scheduler.
|
||||||
|
|
||||||
If @racket[pattern] is supplied, @racket[k-expr] should evaluate to a
|
The state of the yielding process will be matched against
|
||||||
@racket[Transition]; otherwise it should produce an @racket[ActionTree].
|
@racket[state-pattern] when the process is resumed, and
|
||||||
|
@racket[k-expr] must evaluate to a @racket[Transition].
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +708,7 @@ endpoint in the VM's own network, the ground VM:
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(spawn-vm
|
(spawn-vm
|
||||||
(at-meta-level
|
(at-meta-level
|
||||||
(endpoint #:subscriber (tcp-channel ? (tcp-listener 5999) ?) ...)))
|
(subscriber (tcp-channel ? (tcp-listener 5999) ?) ...)))
|
||||||
]
|
]
|
||||||
|
|
||||||
In this example, a new process is spawned as a sibling of the
|
In this example, a new process is spawned as a sibling of the
|
||||||
|
@ -685,7 +717,7 @@ nested VM rather than as a sibling of its primordial process:
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(spawn-vm
|
(spawn-vm
|
||||||
(at-meta-level
|
(at-meta-level
|
||||||
(spawn #:child (transition/no-state (send-message 'hello-world)))))
|
(spawn (transition/no-state (send-message 'hello-world)))))
|
||||||
]
|
]
|
||||||
|
|
||||||
Compare to this example, which spawns a sibling of the
|
Compare to this example, which spawns a sibling of the
|
||||||
|
@ -693,7 +725,7 @@ nested VM's primordial process:
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(spawn-vm
|
(spawn-vm
|
||||||
(spawn #:child (transition/no-state (send-message 'hello-world))))
|
(spawn (transition/no-state (send-message 'hello-world))))
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue