2015-09-13 02:45:49 +00:00
|
|
|
# FAQ
|
|
|
|
|
|
|
|
* How do I run a prospect program?
|
|
|
|
- `#lang prospect` collects actions (`spawn`s) from module toplevel and
|
2016-01-18 19:29:48 +00:00
|
|
|
uses them as boot actions for a ground-level network. The alternative
|
2015-09-13 02:45:49 +00:00
|
|
|
is to use a different #lang, and to call `run-ground` yourself; see an
|
|
|
|
example in prospect/examples/example-plain.rkt.
|
|
|
|
|
2015-09-21 17:31:37 +00:00
|
|
|
* How do I debug a prospect program?
|
2015-09-13 02:45:49 +00:00
|
|
|
- You can view a colored trace of a program's execution on stderr by setting the MINIMART_TRACE environment variable, e.g.
|
|
|
|
|
|
|
|
```
|
2015-09-21 17:31:37 +00:00
|
|
|
$ MINIMART_TRACE=xetpag racket foo.rkt
|
2015-09-13 02:45:49 +00:00
|
|
|
```
|
|
|
|
|
2015-09-21 17:31:37 +00:00
|
|
|
shows
|
|
|
|
x - exceptions
|
|
|
|
e - events
|
|
|
|
t - process states (after they handle each event)
|
|
|
|
p - lifecycle events (spawns, crashes, and quits)
|
|
|
|
a - process actions
|
|
|
|
g - dataspace contents
|
2016-01-18 19:29:48 +00:00
|
|
|
Adding 'N' will show whole network-states too. Remove each individual
|
2015-09-13 02:45:49 +00:00
|
|
|
character to turn off the corresponding trace facility; the default
|
|
|
|
value of the variable is just the empty-string.
|
|
|
|
|
2016-01-22 02:55:41 +00:00
|
|
|
- For a more fine-grained approach, there are several ways to print specific patches/tries inside your program:
|
2015-09-13 02:45:49 +00:00
|
|
|
|
|
|
|
```racket
|
2016-01-22 02:55:41 +00:00
|
|
|
pretty-print-patch ;; (patch trie trie)
|
|
|
|
pretty-print-trie
|
2015-09-13 02:45:49 +00:00
|
|
|
patch->pretty-string
|
2016-01-22 02:55:41 +00:00
|
|
|
trie->pretty-string
|
|
|
|
trie->abstract-graph
|
2015-09-13 02:45:49 +00:00
|
|
|
abstract-graph->dot
|
2016-01-22 02:55:41 +00:00
|
|
|
trie->dot ;; handy for visualizing the trie structure
|
2015-09-13 02:45:49 +00:00
|
|
|
```
|
|
|
|
* How do spawned processes communicate with one another?
|
|
|
|
|
2015-09-21 17:31:37 +00:00
|
|
|
| Expression | Effect of resulting patch | Meaning |
|
|
|
|
| ---------- | -------------------------- | ------- |
|
|
|
|
| (assert X) | X will be asserted | claim of X |
|
|
|
|
| (retract X) | X will be retracted | remove claim |
|
|
|
|
| (sub X) | (observe X) asserted | claim interest in X |
|
|
|
|
| (unsub X) | (observe X) retracted | unsubscribe |
|
|
|
|
| (pub X) | (advertise X) asserted | claim intent to claim X (or possibility of claim) |
|
|
|
|
| (unpub X) | (advertise X) retracted | |
|
2015-09-13 02:45:49 +00:00
|
|
|
|
|
|
|
Those all construct **patch** actions. Separately, there are **message** actions:
|
|
|
|
|
2015-09-21 17:31:37 +00:00
|
|
|
| Expression | Effect of resulting action | Meaning |
|
|
|
|
| ---------- | -------------------------- | ------- |
|
2015-09-13 02:45:49 +00:00
|
|
|
| (message X) | routes X via dataspace via (observe X) assertions | subscribers to X get it |
|
|
|
|
|
|
|
|
* What is the difference between `pub` and `assert`?
|
|
|
|
- `(pub X)` is intended to mean *advertisement* of its body, rather than assertion.
|
|
|
|
So `(assert X)` yields a patch that means "I assert X", while
|
|
|
|
`(pub X)` yields a patch that means "I assert (advertise X)", or
|
|
|
|
interpreted more freely, "I might in future assert X".
|
|
|
|
|
|
|
|
* How do I create a process/actor?
|
|
|
|
```racket
|
|
|
|
;; single actor
|
2015-09-22 18:46:14 +00:00
|
|
|
(spawn (lambda (event state) ... (transition state' (list action ...)))
|
2015-09-13 02:45:49 +00:00
|
|
|
initial-state
|
|
|
|
initial-action ...)
|
2015-09-23 18:56:28 +00:00
|
|
|
;; stateless actor
|
|
|
|
(spawn/stateless (lambda (event) ... (list action ...))
|
|
|
|
initial-action ...)
|
2015-09-13 02:45:49 +00:00
|
|
|
;; network of actors
|
2016-01-18 19:29:48 +00:00
|
|
|
(spawn-network boot-action ...)
|
2015-09-13 02:45:49 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
* How do actors at different levels communicate?
|
|
|
|
- `at-meta` (the harpoon-marker from the paper) denotes "at the next level out" -
|
|
|
|
so, you might send
|
|
|
|
```racket
|
|
|
|
(message 'hello)
|
|
|
|
(message (at-meta 'hello))
|
|
|
|
```
|
|
|
|
and the former would go to your local peers, while the latter would go
|
|
|
|
to peers one level out from you. Likewise,
|
|
|
|
```racket
|
|
|
|
(sub 'hello)
|
|
|
|
(sub 'hello #:meta-level 1)
|
|
|
|
```
|
|
|
|
evaluate to
|
|
|
|
```racket
|
|
|
|
(observe 'hello)
|
|
|
|
(patch-union (observe (at-meta 'hello))
|
|
|
|
(at-meta (observe 'hello)))
|
|
|
|
```
|
|
|
|
|
|
|
|
The latter is a bit surprising-looking perhaps. It asserts **two** items:
|
|
|
|
the first says
|
|
|
|
> HERE, I am interested in assertions 'hello labelled with "OVER THERE"
|
|
|
|
|
|
|
|
while the second says
|
|
|
|
> OVER THERE, I am interested in assertions 'hello
|
|
|
|
|
|
|
|
The former is necessary for the local network to route events to us, and
|
|
|
|
the latter is necessary for the remote network to route events to the
|
|
|
|
local network.
|
|
|
|
|
|
|
|
Implicit in any `sub` call with N>0 meta-level, therefore, is the
|
|
|
|
construction of a whole *chain* of subscriptions, relaying information
|
|
|
|
up in N+1 hops across N+1 boundaries between nested actors.
|
|
|
|
|
|
|
|
* Do I need to keep track of everything a process is asserting in order to retract only what is appropriate?
|
|
|
|
- No. You can often avoid needing to remember state like this explicitly by
|
|
|
|
using wildcard retractions:
|
|
|
|
|
|
|
|
```racket
|
|
|
|
(list (retract `(key ,?))
|
|
|
|
(assert `(key ,new-key-value)))
|
|
|
|
```
|
|
|
|
|
|
|
|
One potential issue with this is it generates two observable events.
|
|
|
|
That is, there is a window of time when the old term has been retracted
|
|
|
|
but the new has not yet been asserted. Implicit in this is the idea that
|
2015-09-21 17:31:37 +00:00
|
|
|
the actions an event-handler produces are performed **in order**, and you
|
2015-09-13 02:45:49 +00:00
|
|
|
can rely on that in NC. So in the example above, the retraction will
|
|
|
|
logically happen before the assertion. You can avoid this by using
|
|
|
|
`patch-seq` instead, to combine patches into a single equivalent patch:
|
|
|
|
|
|
|
|
```racket
|
|
|
|
(patch-seq (retract `(key ,?))
|
|
|
|
(assert `(key ,new-key-value)))
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
* How do I get the assertions out of a patch?
|
2016-01-22 02:55:41 +00:00
|
|
|
- A patch consists of two tries, added and removed
|
|
|
|
- To get assertions out of a trie, you have to decide what sort of assertions
|
2015-09-13 02:45:49 +00:00
|
|
|
you are interested in, compile a pattern for those assertions, and pass that
|
2016-01-22 02:55:41 +00:00
|
|
|
along with the trie to `trie-project/set`.
|
|
|
|
- `trie-project/set` takes a trie and a pattern and returns a set of lists
|
2015-09-13 02:45:49 +00:00
|
|
|
- Say you are in interested in assertions of the shape `('posn x y)`.
|
|
|
|
* compile the pattern using ```(compile-projection `(posn ,(?!) ,(?!)))```
|
|
|
|
* the `(?!)` is for **capturing** the matched value. Use `?` if you want to
|
|
|
|
match but don't care about the actual value.
|
2016-01-22 02:55:41 +00:00
|
|
|
* the lists returned by `trie-project/set` contain the captured values in
|
2015-09-13 02:45:49 +00:00
|
|
|
order.
|
|
|
|
- Say we are receiving a patch p where the assertion `('posn 2 3)` was added.
|
|
|
|
- The result of
|
|
|
|
|
|
|
|
```racket
|
2016-01-22 02:55:41 +00:00
|
|
|
(trie-project/set (patch-added p)
|
|
|
|
(compile-projection `(posn ,(?!) ,(?!))))
|
2015-09-13 02:45:49 +00:00
|
|
|
```
|
|
|
|
would be `(set (list 2 3))`.
|
|
|
|
- If we only cared about the y position, we could instead do
|
|
|
|
|
|
|
|
```racket
|
2016-01-22 02:55:41 +00:00
|
|
|
(trie-project/set (patch-added p)
|
|
|
|
(compile-projection `(posn ,? ,(?!))))
|
2015-09-13 02:45:49 +00:00
|
|
|
```
|
|
|
|
and get the result `(set (list 3))`.
|
2015-10-20 12:34:34 +00:00
|
|
|
- an entire structure can be captured by passing a pattern as an argument to
|
|
|
|
`(?!)`.
|
|
|
|
|
|
|
|
```racket
|
2016-01-22 02:55:41 +00:00
|
|
|
(trie-project/set (patch-added p)
|
|
|
|
(compile-projection (?! `(posn ,? ,?))))
|
2015-10-20 12:34:34 +00:00
|
|
|
```
|
|
|
|
with the same example yields `(set (list ('posn 2 3))`.
|
2016-01-22 02:55:41 +00:00
|
|
|
- `trie-project/set/single` is like mapping `car` over the result of
|
|
|
|
`trie-project/set`. See also `project-assertions`.
|
2015-09-13 02:45:49 +00:00
|
|
|
- `patch-project/set` uses `values` to return the result of matching a projection
|
|
|
|
against both the added and removed bits of a patch.
|
|
|
|
|
|
|
|
* What is the distinction between `(assert X)` and `(message X)`?
|
|
|
|
- Time. An assertion is visible to anyone interested in it from when it is
|
|
|
|
asserted until when it is retracted. A message, on the other hand, is transient.
|
|
|
|
When a message is sent, it is delivered to everyone that is interested in it
|
|
|
|
**at that time**.
|
|
|
|
|
|
|
|
* How to inject an external event (e.g. keyboard press) into the network?
|
|
|
|
- Use `send-ground-message`.
|
|
|
|
- (Note that the argument to `send-ground-message` is wrapped in a `message`,
|
|
|
|
so to send `'foo` at the ground level use `(send-ground-message 'foo)` rather than
|
|
|
|
`(send-ground-message (message 'foo))`)
|
|
|
|
|
2015-09-21 17:31:37 +00:00
|
|
|
* My GUI program isn't working!
|
|
|
|
- Eventspaces. Wrap your GUI code with
|
2015-09-13 02:45:49 +00:00
|
|
|
```racket
|
|
|
|
(parameterize ((current-eventspace (make-eventspace)))
|
|
|
|
...)
|
|
|
|
```
|
|
|
|
|
|
|
|
* I used `spawn` but the actor isn't being created. What happened?
|
|
|
|
- The only two ways to spawn a process are to (a) supply the spawn instruction in
|
2016-01-18 19:29:48 +00:00
|
|
|
that network's boot-actions, or (b) have some already-existing actor supply the
|
2015-09-13 02:45:49 +00:00
|
|
|
spawn instruction in response to some event it receives. Note that calling `spawn`
|
|
|
|
constructs a structure which is perhaps eventually interpreted by the containing
|
2016-01-18 19:29:48 +00:00
|
|
|
network of an actor; it doesn't really "do" anything directly.
|
2015-09-13 02:45:49 +00:00
|
|
|
|
|
|
|
* Why does `patch-seq` exist? Aren't all the actions in a transition effectively `patch-seq`d together?
|
|
|
|
- Effectively, yes, that is what happens. The difference is in the
|
2015-09-21 17:31:37 +00:00
|
|
|
granularity of the action: when issuing *separate patch actions*, it's
|
|
|
|
possible for an observer to observe a moment *in between* the adjacent
|
|
|
|
patches, with the dataspace in an intermediate state.
|
|
|
|
|
|
|
|
Patch-seq combines multiple patches into a single patch having the same
|
|
|
|
effect as the sequence of individual patches.
|
|
|
|
|
|
|
|
By combining a bunch of patch actions into a single action, there is no
|
|
|
|
opportunity for a peer to observe some intermediate state. The peer only
|
|
|
|
gets to observe things-as-they-were-before-the-patch, and
|
|
|
|
things-as-they-are-after-the-patch.
|
2015-09-13 02:45:49 +00:00
|
|
|
|
|
|
|
* How do I create a tiered network, such as
|
|
|
|
```
|
|
|
|
ground
|
|
|
|
/ \
|
|
|
|
net1 net2
|
|
|
|
\
|
|
|
|
net3
|
|
|
|
```
|
2016-01-18 19:29:48 +00:00
|
|
|
- use `spawn-network`:
|
2015-09-13 02:45:49 +00:00
|
|
|
```racket
|
|
|
|
#lang prospect
|
2016-01-18 19:29:48 +00:00
|
|
|
(spawn-network <net1-spawns> ...)
|
|
|
|
(spawn-network <net2-spawns> ...
|
|
|
|
(spawn-network <net3-spawns> ...))
|
2015-09-13 02:45:49 +00:00
|
|
|
```
|
2016-01-18 19:29:48 +00:00
|
|
|
`spawn-network` expands into a regular `spawn` with an event-handler and
|
|
|
|
state corresponding to a whole VM. The arguments to spawn-network are
|
2015-09-13 02:45:49 +00:00
|
|
|
actions to take at boot time in the new VM.
|
|
|
|
|
|
|
|
* What is the outcome if I do `(assert X)` and then later `(patch-seq (retract ?) assert X)`?
|
|
|
|
- if you started with the set Y of assertions, you'd go to Y + {X}, then just {X}.
|
|
|
|
|
|
|
|
* Can a message be included in the initial actions of a process?
|
2015-09-21 17:31:37 +00:00
|
|
|
|
2015-09-13 02:45:49 +00:00
|
|
|
- At the moment they are not allowed in the implementation;
|
2015-09-21 17:31:37 +00:00
|
|
|
this is more restrictive than the calculus, where any action is
|
|
|
|
permitted in a boot action.
|
|
|
|
|
|
|
|
If I remember right, the reason they are not allowed is to do with
|
|
|
|
atomic assignment of responsibilities at spawn time.
|
|
|
|
|
|
|
|
Imagine a socket listener detects some shared state that indicates a new
|
|
|
|
socket is waiting to be accepted. It decides to spawn a process to
|
|
|
|
handle the new connection.
|
|
|
|
|
|
|
|
The protocol for sockets involves maintaining shared state signalling
|
|
|
|
the willingness of each end to continue. Once such a signal is asserted,
|
|
|
|
it must be maintained continuously, because its retraction signals
|
|
|
|
disconnection.
|
|
|
|
|
|
|
|
So the newly-spawned process must signal this state. If it is spawned
|
|
|
|
without the state included in its assertions, then it must assert the
|
|
|
|
state later on. But what if it fails to do so? Then it's violating it's
|
|
|
|
implicit contract with the peer - a kind of denial of service. What if
|
|
|
|
it fails to do so because it *crashes* before it has a chance to? Then
|
|
|
|
the peer will hang forever waiting for something to happen.
|
|
|
|
|
|
|
|
For this reason, it is possible to spawn processes with nonempty
|
|
|
|
assertion sets. The assertions take effect atomically with the creation
|
|
|
|
of the process itself. So the spawning process can atomically "assign
|
|
|
|
responsibility" to the spawned process, giving it no opportunity to
|
|
|
|
crash before the necessary shared state has been asserted.
|
|
|
|
|
|
|
|
The current implementation is problematic though. The computation of the
|
|
|
|
initial patch is being done in the context of the spawned process, which
|
|
|
|
means that if it crashes computing the initial patch, only the spawned
|
|
|
|
process is killed - the spawning process is not signalled. More thought
|
|
|
|
required.
|
2015-09-13 02:45:49 +00:00
|
|
|
|
|
|
|
* Can I split a prospect program across multiple files?
|
|
|
|
- Only one module with `#lang prospect` can be used at a time.
|
2015-09-21 22:31:34 +00:00
|
|
|
|
|
|
|
* Why does `#f` keep getting sent as an event?
|
|
|
|
- When a behavior returns something besides `#f` in response to an event, it is
|
|
|
|
repeatedly sent `#f` until it does return `#f`.
|
|
|
|
- Think of it as a way of the network asking "anything else?"
|