diff --git a/racket/syndicate/scribblings/syndicate.scrbl b/racket/syndicate/scribblings/syndicate.scrbl index 207f258..35afaa3 100644 --- a/racket/syndicate/scribblings/syndicate.scrbl +++ b/racket/syndicate/scribblings/syndicate.scrbl @@ -3,20 +3,107 @@ @(require (for-label (except-in racket process field) syndicate/actor)) -@title{High Level Syntax for Syndicate} +@title{Dataspace Programming with Syndicate} @defmodule[syndicate/actor] -@section{Instantaneous Actions (I)} +@section{Overview} -@defform[(spawn I ...)]{ -Spawns an actor that executes each instantaneous action @racket[I] in -sequence.} +Syndicate is an actor language where all communication occurs through a tightly +controlled shared memory, dubbed the @emph{dataspace}. The values in the +dataspace are called @emph{assertions}, representing the information that the +actors in the system are currently sharing with each other. Assertions are +@emph{read-only} and @emph{owned} by the actor that entered them into the +dataspace. Only the originating actor has permission to withdraw an assertion. +Assertions are linked to the lifetime of their actor, and are withdrawn from the +dataspace when that actor exits, either normally or via exception. + +To respond to assertions in the dataspace, an actor expresses an @emph{interest} +in the shape of assertions it wishes to receive. An interest is an assertion +constructed with @racket[observe] and wildcards where the actor wishes to +receive any matching assertion. When an actor makes an assertion of interest, +the dataspace dispatches the set of all matching assertions to that actor. +Moreover, the dataspace keeps the actor @emph{up-to-date}, informing it when a +new assertion appears matching its interest, as well as when a matching +assertion disappears from the dataspace. Thus, dataspaces implement a form of +publish/subscribe communication. + +@;{would be nice to link pub/sub} + +In addition to assertions, dataspaces support instantaneous @racket[message] +broadcast. At the time a message is sent, all actors with a matching interest +receive notification. + +In response to an event, that is, a message broadcast or assertion +appearance/disappearance matching an expressed interest, a Syndicate actor may +take any of the following actions: +@itemlist[ + @item{Updating its internal state;} + @item{Making or withdrawing assertions;} + @item{Sending broadcast messages;} + @item{Spawning additional actors;} + @item{Exiting;} + @item{Or any combination of these.} +] + +Thus, each individual Syndicate actor has three fudamental concerns: + +@itemlist[ + @item{Defining local state and updating it in response to events;} + @item{Publishing aspects of local state/knowledge as assertions; and} + @item{Reacting to relevant assertions and messages.} +] + +Each concern is addressed by a separate language construct, which are +collectively dubbed @emph{endpoints}: + +@itemlist[ + @item{The @racket[field]s of an actor hold its state;} + @item{An actor publishes information using @racket[assert]; and} + @item{An event-handler endpoint uses @racket[on] to define reactions to + particular messages and assertions.} +] + +Endpoints are tied together via @emph{dataflow}. Thus, the assertions of an +actor automatically reflect the current value of its fields. + +Implementing an actor's role in a particular conversation typically involves +some combination of these behaviors; a @emph{facet} is a collection of related +endpoints constituting the actor's participation in a particular conversation. + +Each actor starts with a single facet, and may add new facets or terminate +current ones in response to events. The facets of an actor form a tree, where +the parent of a particular facet is the facet in which it was created. The tree +structure affects facet shutdown; terminating a facet also terminates all of its +descendants. + +To recap: an actor is a tree of facets, each of which comprises of a collection +of endpoints. + +@section{Programming Syndicate Actors with Facets} + +Code within Syndicate actors executes in one of two contexts: +@itemlist[ + @item{The @emph{endpoint-installation} context occurs during the creation of a + new facet, when all of its endpoints are created.} + @item{The @emph{script} context occurs during the execution of event handlers, + and permits creating/terminating facets, sending messages, and spawning + actors.} +] + +The actions permitted by the two contexts are mutually exclusive, and trying to +perform an action in the wrong context will give rise to a run-time +@racket[error]. + +Within the following descriptions, we use @emph{EI} as a shorthand for +expressions that execute in an endpoint-installation context and @emph{S} for +expressions in a script context. + +@defform[(spawn EI ...)]{ +Spawn an actor with a single inital facet whose endpoints are installed by +@racket[EI]. That is, there is an implicit @racket[react] around @racket[EI ...].} -@defform[(dataspace I ...)]{ -Spawns a dataspace as a child of the dataspace enclosing the executing actor. The -new dataspace executes each instantaneous action @racket[I].} @defproc[(send! [v any/c] [#:meta-level level natural-number/c 0]) @@ -25,84 +112,6 @@ Sends a message with body @racket[v]. The message is sent @racket[level] dataspaces removed from the dataspace containing the actor performing the @racket[send!].} -@defproc[(assert! [v any/c] - [#:meta-level level natural-number/c 0]) - void?]{ -Asserts the value of @racket[v] until either explicitly retracted via -@racket[retract!] or the immediately enclosing actor exits. @racket[level] -specifies which dataspace the assertion should be made, in terms of relative -distance from the dataspace containing the enclosing actor.} - -@defproc[(retract! [v any/c] - [#:meta-level level natural-number/c 0]) - void?]{ -Retracts any assertions made by the immediately enclosing actor at -@racket[level] dataspaces above the enclosing dataspace of the form @racket[v].} - -@section{Ongoing Behaviors (O)} - -@defform[(state maybe-init (maybe-bindings O ...) ([E I ...] ...)) - #:grammar - [(maybe-init (code:line) - (code:line #:init [I ...])) - (maybe-bindings (code:line) - (code:line #:collect ([id init] ...)))] - #:contracts ([id identifier?])]{ -Spawns a new actor with ongoing behaviors @racket[O ...] that runs until a -termination event is detected. - -The optional @racket[#:init [I ...]] provides a sequence of initialization -actions. The initial actions are executed before the ongoing behaviors begin but -after the interests of the state actor are established. - -The optional @racket[#:collect [(id init) ...]] clause introduces bindings that -are visible within the body of the state actor. Each binding @racket[id] is -initialized to the corresponding @racket[init] expression. The bindings are -updated when an ongoing behavior executes an instantaneous event, such as the -result of an @racket[on] behavior. The new bindings are in the form of a -@racket[values] form, with the new values in the same order and number as in the -@racket[#:collect]. - -The ongoing behaviors @racket[O ...] are run simultaneously until the state -actor exits. - -Each @racket[[E I ...]] specifies a termination event @racket[E] of the actor. -When a termination event @racket[E] activates, the corresponding @racket[I]s are -executed. The state actor then exits, with the same result of the final -@racket[I] action.} - -@defform[(until E - maybe-init - maybe-bindings - maybe-done - O ...) - #:grammar - [(maybe-init (code:line) - (code:line #:init [I ...])) - (maybe-bindings (code:line) - (code:line #:collect ([id init] ...))) - (maybe-done (code:line) - (code:line #:done [I ...]))] - #:contracts ([id identifier?])]{ -An @racket[until] behavior corresponds to a @racket[state] behavior with only -one termination event, given by @racket[E]. The final result of the -@racket[until] behavior is the values of the @racket[#:collect] bindings in -scope from any parent actors followed by the final values of the @racket[until] -actor's bindings. The actions in a @racket[#:done] clause are executed after the -termination event but before the @racket[until] actor exits.} - -@defform[(forever maybe-init - maybe-bindings - O ...) - #:grammar - [(maybe-init (code:line) - (code:line #:init [I ...])) - (maybe-bindings (code:line) - (code:line #:collect ([id init] ...)))] - #:contracts ([id identifier?])]{ -The @racket[forever] behavior is analogous to a @racket[state] form with no -termination events.} - @defform[(during pat O ...)]{ Runs the behaviors @racket[O ...] for the duration of each assertion matching @racket[pat]. @@ -178,3 +187,88 @@ to @racket[id]. @racket[expr] patterns match values that are exactly equal to @racket[expr]. +@section{Actors with an Agenda} + +Here we talk about @racket[spawn*] and @racket[react/suspend]. + +@section{Odds and Ends} + +@defform[(dataspace I ...)]{ +Spawns a dataspace as a child of the dataspace enclosing the executing actor. The +new dataspace executes each instantaneous action @racket[I].} + +@defproc[(assert! [v any/c] + [#:meta-level level natural-number/c 0]) + void?]{ +Asserts the value of @racket[v] until either explicitly retracted via +@racket[retract!] or the immediately enclosing actor exits. @racket[level] +specifies which dataspace the assertion should be made, in terms of relative +distance from the dataspace containing the enclosing actor.} + +@defproc[(retract! [v any/c] + [#:meta-level level natural-number/c 0]) + void?]{ +Retracts any assertions made by the immediately enclosing actor at +@racket[level] dataspaces above the enclosing dataspace of the form @racket[v].} + +@defform[(state maybe-init (maybe-bindings O ...) ([E I ...] ...)) + #:grammar + [(maybe-init (code:line) + (code:line #:init [I ...])) + (maybe-bindings (code:line) + (code:line #:collect ([id init] ...)))] + #:contracts ([id identifier?])]{ +Spawns a new actor with ongoing behaviors @racket[O ...] that runs until a +termination event is detected. + +The optional @racket[#:init [I ...]] provides a sequence of initialization +actions. The initial actions are executed before the ongoing behaviors begin but +after the interests of the state actor are established. + +The optional @racket[#:collect [(id init) ...]] clause introduces bindings that +are visible within the body of the state actor. Each binding @racket[id] is +initialized to the corresponding @racket[init] expression. The bindings are +updated when an ongoing behavior executes an instantaneous event, such as the +result of an @racket[on] behavior. The new bindings are in the form of a +@racket[values] form, with the new values in the same order and number as in the +@racket[#:collect]. + +The ongoing behaviors @racket[O ...] are run simultaneously until the state +actor exits. + +Each @racket[[E I ...]] specifies a termination event @racket[E] of the actor. +When a termination event @racket[E] activates, the corresponding @racket[I]s are +executed. The state actor then exits, with the same result of the final +@racket[I] action.} + +@defform[(until E + maybe-init + maybe-bindings + maybe-done + O ...) + #:grammar + [(maybe-init (code:line) + (code:line #:init [I ...])) + (maybe-bindings (code:line) + (code:line #:collect ([id init] ...))) + (maybe-done (code:line) + (code:line #:done [I ...]))] + #:contracts ([id identifier?])]{ +An @racket[until] behavior corresponds to a @racket[state] behavior with only +one termination event, given by @racket[E]. The final result of the +@racket[until] behavior is the values of the @racket[#:collect] bindings in +scope from any parent actors followed by the final values of the @racket[until] +actor's bindings. The actions in a @racket[#:done] clause are executed after the +termination event but before the @racket[until] actor exits.} + +@defform[(forever maybe-init + maybe-bindings + O ...) + #:grammar + [(maybe-init (code:line) + (code:line #:init [I ...])) + (maybe-bindings (code:line) + (code:line #:collect ([id init] ...)))] + #:contracts ([id identifier?])]{ +The @racket[forever] behavior is analogous to a @racket[state] form with no +termination events.}