Syndicated Actor Model essay

This commit is contained in:
Tony Garnock-Jones 2022-03-02 16:36:38 +01:00
parent 3a537da41f
commit 286c5997b9
3 changed files with 494 additions and 12 deletions

View File

@ -87,15 +87,6 @@
# Specifications and Theory
- [Overview](./theory/index.md)
- [Syndicated Actor Model]()
- [Actors, Entities, Assertions and Messages]()
- [Dataspaces]()
- [Object-capabilities]()
- [Privilege separation]()
- [Attenuation of authority for security]()
- [Attenuation of authority for privacy]()
- [Syndicated Actor Model](./syndicated-actor-model.md)
- [Protocol specification](./protocol.md)
- [System layer analysis]()

View File

@ -0,0 +1,490 @@
# Syndicated Actor Model
The Syndicated Actor Model (SAM) [[Garnock-Jones 2017][]] is an approach to concurrency based
on the *Communicating Event-Loop Actor Model* [[De Koster et al 2016][]] as pioneered by E
[[Miller 2006][]] and AmbientTalk [[Van Cutsem et al 2007][]].
While other Actor-like models take message-passing as fundamental, the SAM builds on a
different underlying primitive: *eventually-consistent replication of state* among actors.
Message-passing follows as a derived operation.
This fundamental difference integrates Tuplespace- [[Gelernter and Carriero 1992][]] and
publish/subscribe-like ideas with concurrent object-oriented programming, and makes the SAM
well-suited for building programs and systems that are reactive, robust to change, and graceful
in the face of partial failure.
**Outline.** This document first describes the primitives of SAM interaction, and then briefly
illustrates their application to distributed state management and handling of partial failure.
It goes on to present the idea of a *dataspace*, an integration of Tuplespace- and
publish/subscribe-like ideas with the SAM. Finally, it discusses the SAM's generalization of
*object capabilities* to allow for control not only over invocation of object behaviour but
subscription to object state.
Throughout, we will limit discussion to interaction among actors connected directly to one
another: that is, to interaction within a single scope. Scopes can be treated as "subnets" and
connected together: see the [Syndicate protocol specification](./protocol.md).
For more on the SAM, on the concept of "conversational concurrency" that the model is a
response to, and on other aspects of the larger project that the SAM is a part of, please see
<https://syndicate-lang.org/about/> and Garnock-Jones' [2017 dissertation][Garnock-Jones 2017].
## Concepts and components of SAM interaction
A number of inter-related ideas must be taken together to make sense of SAM interaction. This
section will outline the essentials.
For core concepts of Actor models generally, see De Koster *et al.*'s outstanding [2016 survey
paper][De Koster et al 2016], which lays out a taxonomy of Actor systems as well as introducing
solid definitions for terms such as "actor", "message", and so on.
### Actors, Entities, Assertions and Messages
The SAM is based around actors which not only exchange messages, but publish ("assert")
selected portions of their internal state ("assertions") to their peers in a publish/subscribe,
reactive manner. Assertions and messages in the SAM are [semi-structured
data](https://en.wikipedia.org/wiki/Semi-structured_data): their structure allows for
pattern-matching and [content-based
routing](https://www.enterpriseintegrationpatterns.com/ContentBasedRouter.html).
Assertions are published and withdrawn freely throughout each actor's lifetime. When an actor
terminates, all its published assertions are automatically withdrawn. This holds for both
normal and exceptional termination: crashing actors are cleaned up, too.
An actor in the SAM comprises
- an *inbox*, for receiving events from peers;
- a *state*, "all the state that is synchronously accessible by that actor" ([De Koster et al 2016][]);
- a collection of *entities*; and
- a collection of *outbound assertions*, the data to be automatically retracted upon actor termination.
The term "entity" in the SAM denotes a reactive object, owned by a specific
actor.[^terminology-vs-e] Entities, not actors, are the unit of addressing in the SAM. Every
published assertion and every sent message is targeted at some entity. Entities never outlive
their actors—when an actor terminates, its entities become unresponsive—but may have lifetimes
shorter than their owning actors.
Local interactions, among objects (entities) within the state of the same actor, occur
synchronously. All other interactions are considered "remote", and occur asynchronously.
**"Message" as a derived concept.** A minimal theoretical Syndicated Actor Model includes
*only* assertion publication and retraction. Message transmission is excluded because,
theoretically, it can be *defined* as a "brief" assertion of the message body; that is, as an
assertion, followed "almost immediately" by a matching retraction. This naturally captures a
number of interesting properties of messages, including potential message loss and corruption
as well as issues related to medium access control. While it is useful theoretically to view
message exchange as a pattern (in the "software design pattern" sense) of assertion usage, the
pattern is so common in practical programming that the SAM and its implementations include
integrated support for messages as standard.[^analogy-with-function-call]
### Turns
Each time an event arrives at an actor's inbox, the actor takes a *turn*. [De Koster *et
al.*][De Koster et al 2016] define turns as follows:
> A turn is defined as the processing of a single message by an actor. In other words, a turn
> defines the process of an actor taking a message from its inbox and processing that message
> to completion.
In the SAM, a turn comprises
- the *event* that triggers the turn and the *entity* addressed by the event,
- the entity's *execution* of its response to the event, and
- the collection of pending *actions* produced during execution.
If a turn proceeds to completion without an exception or other crash, its pending actions are
delivered to their target entities/actors. If, on the other hand, the turn is aborted for some
reason, its pending actions are discarded. This transactional "commit" or "rollback" of a turn
is familiar from other event-loop-style models such as Ken [[Yoo et al 2012][]].
### Events and Actions
SAM *events* convey a new assertion, retraction of a previously-established assertion, delivery
of a message, or a request for synchronisation.
In response to an event, an actor (entity) schedules *actions* to be performed at the end of
the turn. Actions include not only publication and retraction of assertions, transmission of
messages, and issuing of synchronisaton requests, but also termination of the running actor and
creation of new actors to run alongside the running actor.
### Entity References are Object Capabilities
As mentioned above, entities are the unit of addressing in the SAM. Assertions and message
bodies may include *references* to entities. Actors receiving such references may then use them
as targets for later assertions and messages. Entity references act as [*object
capabilities*](https://en.wikipedia.org/wiki/Object-capability_model), very similar to those
offered by E [[Miller 2006][]].
Entity references play many roles in SAM interactions, but two are of particular importance.
First, entity references are used to simulate *functions* and *continuations* for encoding
remote procedure calls (RPCs). Second, entity references can act like *consumers* or
*subscribers*, receiving asynchronous notifications about state changes from peers.
## Illustrative Examples
To show some of the potential of the SAM, we will explore two representative examples: a
distributed spreadsheet, and a cellular modem server.
### Spreadsheet cell
Imagine a collection of actors representing portions of a spreadsheet, each containing entities
representing spreadsheet cells. Each cell entity publishes public aspects of its state to
interested peers: namely, its current value. It also responds to messages instructing it to
update its formula. In pseudocode:
    ` 1:`**define entity** Cell(*formula*):
    ` 2:`    *subscribers* ← ∅
    ` 3:`    **on** assertion from a peer of interest in our value,
    ` 4:`        add *peer*, the entity reference carried in the assertion of interest, to *subscribers*
    ` 5:`    **on** retraction of previously-expressed interest from some *peer*,
    ` 6:`        remove *peer* from *subscribers*
    ` 7:`    **assert** subscriptions to other Cells (using entity references in *formula*)
    ` 8:`    **on** message conveying a new formula,
    ` 9:`        *formula**newFormula*
    `10:`        **replace** subscription assertions using references in new *formula*
    `11:`    **on** assertion conveying updated contents relevant to *formula*,
    `12:`        *value* ← eval(*formula*)
    `13:`    **continuously**, whenever *value* or *subscribers* changes,
    `14:`        **assert** the contents of *value* to every peer in *subscribers*,
    `15:`        **retract**ing previously-asserted values
Much of the subscription-management behaviour of Cell is generic: lines 26 managing the
*subscribers* set and lines 1314 iterating over it will be common to any entity wishing to
allow observers to track portions of its state. This observation leads to the factoring-out of
*dataspaces*, introduced below.
### Cellular modem server
Imagine an actor implementing a simple driver for a cellular modem, that accepts requests (as
Hayes modem command strings) paired with *continuations* represented as entity references. Any
responses the modem sends in reply to a command string are delivered to the continuation entity
as a SAM message.
    ` 1:`**define entity** Server():
    ` 2:`    **on** assertion Request(*commandString*, *replyEntity*)
    ` 3:`        output *commandString* via modem serial port
    ` 4:`        collect response(s) from modem serial port
    ` 5:`        **send** response(s) as a message to *replyEntity*
    ` 6:`**define entity** Client(*serverRef*):
    ` 7:`    **define entity** *k*:
    ` 8:`        **on** message containing responses,
    ` 9:`            **retract** the Request assertion
    `10:`            (and continue with other tasks)
    `11:`    **assert** Request(`"AT+CMGS=..."`, *k*) to *serverRef*
This is almost a standard continuation-passing style encoding of remote procedure
call.[^rpc-sam-discussion] However, there is one important difference: the request is sent to
the remote object not as a message, but as an *assertion*. Assertions, unlike messages, have a
*lifetime* and so can act to set up a [conversational
frame](https://syndicate-lang.org/tonyg-dissertation/html/#x_2_2_0_0_1) within which further
interaction can take place.
Here, subsequent interaction appears at first glance to be limited to transmission of a
response message to *replyEntity*. But what if the Server were to crash before sending a
response?
Erlang [[Armstrong 2003][]] pioneered the use of "links" and "monitors" to detect failure of a
remote peer during an interaction; "broken promises" and a suite of special system messages
such as `__whenBroken` and `__reactToLostClient` [[Miller 2006][], chapter 17] do the same for
E. The SAM instead uses *retraction of previous assertions* to signal failure.
To see how this works, we must step away from the pseudocode above and examine the context
where *serverRef* is discovered for eventual use with Client. In the case that an assertion,
rather than a message, conveys *serverRef* to the client actor, then when Server *crashes*, the
assertion conveying *serverRef* is automatically retracted. The client actor, interpreting this
as failure, can choose to respond appropriately.
The ubiquity of these patterns of service discovery and failure signalling also contributed,
along with the patterns of generic publisher/subscriber state management mentioned above, to
the factoring-out of dataspaces.
## Dataspaces
A special kind of syndicated actor entity, a *dataspace*, routes and replicates published data
according to actors' interests.
    ` 1:`**define entity** Dataspace():
    ` 2:`    *allAssertions* ← ∅
    ` 3:`    *allSubscribers* ← ∅
    ` 4:`    **on** assertion of semi-structured datum *a*,
    ` 5:`        add *a* to *allAssertions*
    ` 6:`        if *a* matches Observe(*pattern*, *subscriberRef*),
    ` 7:`            add (*pattern*, *subscriberRef*) to *allSubscribers*
    ` 8:`            for *x* in *allAssertions*, if *x* matches *pattern*,
    ` 9:`                **assert** *x* at *subscriberRef*
    `10:`        otherwise,
    `11:`            for (*p*, *s*) in *allSubscribers*, if *a* matches *p*,
    `12:`                **assert** *a* at *s*
    `13:`    **on** retraction of previously-asserted *a*,
    `14:`        retract *a* from all subscribers to whom it was forwarded
    `15:`        remove *a* from *allAssertions*
    `16:`        if *a* matches Observe(*pattern*, *subscriberRef*),
    `17:`            remove (*pattern*, *subscriberRef*) from *allSubscribers*
    `18:`            retract all assertions previously sent to *subscriberRef*
Assertions sent to a dataspace are routed by pattern-matching. Subscriptions—tuples associating
a pattern with a subscriber entity—are placed in the dataspace as assertions like any other.
A dataspace entity behaves very similarly to a tuplespace [[Gelernter and Carriero 1992][]].
One key difference is that tuples in a tuplespace are "generative" [[Gelernter 1985][]], taking
on independent existence once created and potentially remaining in a tuplespace indefinitely;
SAM assertions, by contrast, never outlive their asserting actors.
Assertions placed at a dataspace only exist as long as they are actively maintained: if an
actor terminates or crashes, *all* its assertions are withdrawn, including those targeted at a
dataspace entity. The dataspace, following its definition, forwards all withdrawals on to
interested subscribers.
### Applications of dataspaces
Dataspaces have many uses. They are ubiquitous in SAM programs. The form of state replication
embodied in dataspaces subsumes Erlang-style links and monitors, publish/subscribe,
tuplespaces, presence notifications, directory/naming services, and so on.
#### Subscription management
The very essence of a dataspace entity is subscription management. Entities wishing to manage
collections of subscribers can cooperate with dataspaces: they may either manage a private
dataspace entity, or share a dataspace with other entities. For example, in the spreadsheet
cell example above, each cell could use its own private dataspace, or all cells could share a
dataspace by embedding their values in a record alongside some name for the cell.
#### Service directory and service discovery
Assertions placed at a dataspace may include entity references. This makes a dataspace an ideal
implementation of a service directory. Services advertise their existence by asserting *service
presence* [[Konieczny et al 2009][]] records including their names alongside relevant entity
references:
    Service(`"name"`, *serviceRef*)
Clients discover services by asserting *interest* in such records using patterns:
    Observe(⌜Service(`"name"`, _)⌝, *clientRef*)
Whenever some matching Service record has been asserted by a server, the dataspace asserts the
corresponding record to *clientRef*. (The real dataspace pattern language includes *binding*,
not discussed here; see [TODO].)
#### Failure signalling
Since assertions of service presence are withdrawn on failure, and withdrawals are propagated
to interested subscribers, service clients like *clientRef* above will be automatically
notified whenever *serviceRef* goes out of service. The same principle can also be applied in
other similar settings.
#### Independence from service identity
There's no need to separate service discovery from service interaction. A client may assert its
request directly at the dataspace; a service may subscribe to requests in the same direct way:
    `(client:)` ServiceRequest(`"name'`, *arg1*, *arg2*, ..., *replyRef*)
    `(server:)` Observe(⌜ServiceRequest(`"name'`, *?a*, *?b*, ..., *?k*)⌝, *serviceRef*)
In fact, there are benefits to doing things this way. If the service should crash
mid-transaction, then when it restarts, the incomplete ServiceRequest record will remain, and
it can pick up where it left off. The client has become decoupled from the specific identity of
the service provider, allowing flexibility that wasn't available before.
#### Asserting interest in assertions of interest
Subscriptions at a dataspace are assertions like any other. This opens up the possibility of
subscribing to *subscriptions*:
    Observe(⌜Observe(⌜...⌝, _)⌝, *r*)
This allows dataspace subscribers to express interest in *which other subscribers are present*.
In many cases, explicit assertion of presence (via, e.g., the Service records above) is the
right thing to do, but from time to time it can make sense for clients to treat the presence of
some subscriber interested in their requests as sufficient indication of service presence to go
ahead.[^background-on-interests]
## Illustrative Examples revisited
Now that we have Dataspaces in our toolbelt, let's revisit the spreadsheet cell and cellular
modem examples from above.
### Spreadsheet cell with a dataspace
    ` 1:`**define entity** Cell(*dataspaceRef*, *name*, *formula*):
    ` 2:`    **continuously**, whenever *value* changes,
    ` 3:`        **assert** CellValue(*name*, *value*) to *dataspaceRef*
    ` 4:`    **continuously**, whenever *formula* changes,
    ` 5:`        for each name *n* in *formula*,
    ` 6:`            define entity *k*:
    ` 7:`                **on** assertion of *nValue*,
    ` 8:`                    *value* ← (re)evaluation based on *formula*, *nValue*, and other *nValue*s
    ` 9:`            **assert** Observe(⌜CellValue(*n*, *?nValue*)⌝, *k*) to *dataspaceRef*
    `10:`    **on** message conveying a new formula,
    `11:`        *formula* ← *newFormula*
The cell is able to outsource all subscription management to the *dataspaceRef* it is given.
Its behaviour function is looking much closer to an abstract prose specification of a
spreadsheet cell.
### Cellular modem server with a dataspace
There are many ways to implement RPC using dataspaces,[^rpc-sam-discussion] each with different
characteristics. This implementation uses anonymous service instances, implicit service names,
asserted requests, and message-based responses:
    ` 1:`**define entity** Server(*dataspaceRef*):
    ` 2:`    **define entity** *serviceRef*:
    ` 3:`        **on** assertion of *commandString* and *replyEntity*
    ` 4:`            output *commandString* via modem serial port
    ` 5:`            collect response(s) from modem serial port
    ` 6:`            **send** response(s) as a message to *replyEntity*
    ` 7:`    **assert** Observe(⌜Request(*?commandString*, *?replyEntity*)⌝, *serviceRef*) to *dataspaceRef*
    ` 8:`**define entity** Client(*dataspaceRef*):
    ` 9:`    **define entity** *k*:
    `10:`        **on** message containing responses,
    `11:`            **retract** the Request assertion
    `12:`            (and continue with other tasks)
    `13:`    **assert** Request(`"AT+CMGS=..."`, *k*) to *dataspaceRef*
If the service crashes before replying, the client's request remains outstanding, and a service
supervisor [[Armstrong 2003][], section 4.3.2] can reset the modem and start a fresh service
instance. The client remains blissfully unaware that anything untoward happened.
## Object-capabilities for access control
[Object capabilities](https://en.wikipedia.org/wiki/Object-capability_model) are the only
properly compositional way to secure a distributed system. They are a natural fit for
Actor-style systems, as demonstrated by E and its various descendants [[Miller 2006][], [Van
Cutsem et al 2007][], [Stiegler and Tie 2010][], [Yoo et al 2012][] and others], so it makes sense
that they would work well for the Syndicated Actor Model.
The main difference between SAM capabilities and those in E-style Actor models is that
syndicated capabilities express pattern-matching-based restrictions on the *assertions* that
may be directed toward a given entity, as well as the *messages* that may be sent its way.
Combined with the fact that subscription is expressed with assertions like any other, this
yields a mechanism offering control over state replication and observation of replicated state
as well as ordinary message-passing and RPC.
In the SAM, a capability is a triple of
- target actor reference,
- target entity reference within that actor, and
- an *attenuation* describing accepted assertions and messages.
An "attenuation" is a piece of syntax including patterns over semi-structured data. When an
assertion or message is directed to the underlying entity by way of an attenuated capability,
the asserted value or message body is checked against the patterns in the attenuation. Values
not matching are discarded silently.
**Restricting method calls.** For example, a reference to the dataspace where our cellular
modem server example is running could be attenuated to only allow assertions of the form
Request(`"ATA"`, _). This would have the effect of limiting holders of the capability to only
being able to cause the modem to answer an incoming call ("ATA").
**Restricting subscriptions.** As another example, a reference to the dataspace where our
spreadsheet cells are running could be attenuated to only allow assertions of the form
Observe(⌜CellValue(`"B13"`, _)⌝, _). This would have the effect of limiting holders of the
capability to only being able to read the contents (or presence) of cell B13.
## Conclusion
We have looked at the concepts involved in the Syndicated Actor Model (SAM), an Actor-like
approach to concurrency that offers a form of concurrent object-oriented programming with
intrinsic publish/subscribe support. The notion of a *dataspace* factors out common interaction
patterns and decouples SAM components from one another in useful ways. Object capabilities are
used in the SAM not only to restrict access to the behaviour offered by objects, but to
restrict the kinds of subscriptions that can be established to the state published by SAM
objects.
While we have examined some of the high level forms of interaction among entities residing in
SAM actors, we have not explored techniques for effectively structuring the *internals* of such
actors. For this, the SAM offers the concept of "facets", which relate directly to
conversational contexts; for a discussion of these, see Garnock-Jones' [2017
dissertation][Garnock-Jones 2017], especially [chapter
2](https://syndicate-lang.org/tonyg-dissertation/html/#x_2_5_0_0_25), [chapter
5](https://syndicate-lang.org/tonyg-dissertation/html/#CHAP:COMPUTATIONAL-MODEL-II:SYNDICATE),
[chapter 8](https://syndicate-lang.org/tonyg-dissertation/html/#CHAP:IDIOMATIC-SYNDICATE) and
[section 11.1](https://syndicate-lang.org/tonyg-dissertation/html/#sec:Placing-PL-on-the-map).
A less formal [discussion of
facets](https://syndicate-lang.org/about/#facets-represent-sub-conversations) can also be found
on the Syndicate project website.
## Bibliography
[Armstrong 2003]: #ref:armstrong-2003
[**Armstrong 2003**] <span id="ref:armstrong-2003">Armstrong, Joe. “Making Reliable Distributed
Systems in the Presence of Software Errors.” PhD, Royal Institute of Technology,
Stockholm, 2003. [[PDF]](https://erlang.org/download/armstrong_thesis_2003.pdf)</span>
[De Koster et al 2016]: #ref:de-koster-2016
[**De Koster et al 2016**] <span id="ref:de-koster-2016">De Koster, Joeri, Tom Van Cutsem, and
Wolfgang De Meuter. “43 Years of Actors: A Taxonomy of Actor Models and Their Key Properties.”
In Proc. AGERE. Amsterdam, The Netherlands, 2016. [[DOI (PDF
available)]](https://doi.org/10.1145/3001886.3001890)</span>
[Garnock-Jones 2017]: #ref:garnock-jones-2017
[**Garnock-Jones 2017**] <span id="ref:garnock-jones-2017">Garnock-Jones, Tony. “Conversational
Concurrency.” PhD, Northeastern University, 2017.
[[PDF]](https://syndicate-lang.org/papers/conversational-concurrency-201712310922.pdf)
[[HTML]](https://syndicate-lang.org/tonyg-dissertation/html)</span>
[Gelernter 1985]: #ref:gelernter-1985
[**Gelernter 1985**] <span id="ref:gelernter-1985">Gelernter, David. “Generative Communication
in Linda.” ACM TOPLAS 7, no. 1 (January 2, 1985): 80112.
[[DOI]](https://doi.org/10.1145/2363.2433)</span>
[Gelernter and Carriero 1992]: #ref:gelernter-1992
[**Gelernter and Carriero 1992**] <span id="ref:gelernter-1992">Gelernter, David, and Nicholas
Carriero. “Coordination Languages and Their Significance.” Communications of the ACM 35, no. 2
(February 1, 1992): 97107. [[DOI]](https://doi.org/10.1145/129630.129635)</span>
[Konieczny et al 2009]: #ref:konieczny-2009
[**Konieczny et al 2009**] <span id="ref:konieczny-2009">Konieczny, Eric, Ryan Ashcraft, David
Cunningham, and Sandeep Maripuri. “Establishing Presence within the Service-Oriented
Environment.” In IEEE Aerospace Conference. Big Sky, Montana, 2009.
[[DOI]](https://doi.org/10.1109/AERO.2009.4839647)</span>
[Miller 2006]: #ref:miller-2006
[**Miller 2006**] <span id="ref:miller-2006">Miller, Mark S. “Robust Composition: Towards a
Unified Approach to Access Control and Concurrency Control.” PhD, Johns Hopkins
University, 2006. [[PDF]](http://www.erights.org/talks/thesis/markm-thesis.pdf)</span>
[Stiegler and Tie 2010]: #ref:stiegler-2010
[**Stiegler and Tie 2010**] <span id="ref:stiegler-2010">Stiegler, Marc, and Jing Tie.
“Introduction to Waterken Programming.” Technical Report. Hewlett-Packard Labs, August 6, 2010.
[[Available online]](https://www.hpl.hp.com/techreports/2010/HPL-2010-89.html)</span>
[Van Cutsem et al 2007]: #ref:van-cutsem-2007
[**Van Cutsem et al 2007**] <span id="ref:van-cutsem-2007">Van Cutsem, Tom, Stijn Mostinckx,
Elisa González Boix, Jessie Dedecker, and Wolfgang De Meuter. “AmbientTalk: Object-Oriented
Event-Driven Programming in Mobile Ad Hoc Networks.” In Proc. XXVI Int. Conf. of the Chilean
Soc. of Comp. Sci. (SCCC07). Iquique, Chile, 2007.
[[DOI]](https://doi.org/10.1109/SCCC.2007.12)
[Yoo et al 2012]: #ref:yoo-2012
[**Yoo et al 2012**] <span id="ref:yoo-2012">Yoo, Sunghwan, Charles Killian, Terence Kelly,
Hyoun Kyu Cho, and Steven Plite. “Composable Reliability for Asynchronous Systems.” In Proc.
USENIX Annual Technical Conference. Boston, Massachusetts, 2012.
[[Talk]](https://www.usenix.org/conference/atc12/technical-sessions/presentation/yoo)
[[PDF]](https://www.usenix.org/system/files/conference/atc12/atc12-final206-7-20-12.pdf)
[[Project page]](https://web.eecs.umich.edu/~tpkelly/Ken/)</span>
----
#### Notes
[^terminology-vs-e]: The terminology used in the SAM connects to the names used in E [[Miller
2006][]] as follows: our *actors* are E's *vats*; our *entities* are E's *objects*.
[^analogy-with-function-call]: Consider the analogy to a similarly-fundamental design pattern:
function call. It can be usefully expressed in simpler terms, which expose interesting
aspects of and variations upon the pattern, but its utility makes it ubiquitous enough to
deserve special support not only from most programming languages, but from most *hardware*.
[^rpc-sam-discussion]: Many variations on RPC are discussed in section 8.7 of Garnock-Jones'
[2017 dissertation][Garnock-Jones 2017] ([direct link to relevant section of online
text](https://syndicate-lang.org/tonyg-dissertation/html/#sec:RPC)).
[^background-on-interests]: For more on assertions of interest, see
[here](https://syndicate-lang.org/about/#conversational-frames-conversational-knowledge) and
[here](https://syndicate-lang.org/tonyg-dissertation/html/#x_2_2_0_0_8).

View File

@ -1,5 +1,6 @@
# Specifications and Theory
TODO: links
TODO: intro
[Protocol](../protocol.md)
- [Syndicated Actor Model](../syndicated-actor-model.md)
- [Protocol](../protocol.md)