Dynamic Behaviors in RDP

RDP behaviors may be constructed and executed at runtime to extend a static program. Dynamic behaviors essential for many in-the-large RDP design patterns:

  1. pluggable extensibility – a service or application can publish dynamic behaviors into a shared registry, allowing subsystems observing that registry to begin using it at their own discretion. The converse is also true: a service can observe a shared registry to find dependencies.
  2. latency reduction – high-latency behaviors can return references to low-latency behaviors. This models compilation, for example, and service brokering. First-class behaviors allow us to eliminate middle-men from the chain, and escape the trend of monotonic latency.
  3. lazy, incremental, progressive systems – respond to a query with a low-resolution summary, plus references to learn more. In UI terms, these might correspond to hyperlinks, drill-down widgets, level-of-detail. A service can more precisely provide whatever data is necessary, and thus waste less time directing searches and sensors to gather other data.
  4. mutable views – behaviors for controlling a system can be in response to a query, allowing us to influence the view in manners far more precise and deliberate than mere observer-effects. This is a very convenient model for composition and safety, especially when combined with incremental views that clarify an operator’s focus.
  5. delegation, attenuation, and revocation – RDP is securable via the object capability model, with dynamic behaviors serving as capabilities. Dynamic behaviors can be composed, filtered, shared, attenuated. Due to the stateless nature of RDP, every shared dynamic behavior is revocable (simply stop sharing it), which greatly simplifies reasoning about security (no worry about history, grandfathered capabilities).
  6. adaptive, ambient, and mobile programming – registries may be geolocated, modeling local and logical environments. This allows applications to adapt reactively to available resources, and to provide services to or communicate with other nearby applications. Useful for mobile computing, augmented reality, and mobile agent models.

Constraints on Dynamic Behaviors in RDP

Dynamic behaviors in RDP are not first class. Use is constrained in ways that forcibly distinguish dynamic behaviors from static behaviors and arbitrary values:

  1. static delay: dynamic delay is allowed, but fluctuation in latency between demand and response would violate duration coupling – an essential invariant of RDP. Thus, every RDP behavior must have static observable delay. This restricts how dynamic behaviors are embedded into the workflow of the larger system.
  2. discrete update: there are set-up and tear-down costs for establishing each dynamic behavior. Such costs can only be paid a finite number of times in any given period, so all changes in dynamic behavior must be discrete (occurring at specific instants in time). Effectively, dynamic RDP behaviors are staged static RDP behaviors. Unlike the others, this is not an ‘essential’ property, though… I just don’t know how to implement continuous dynamic behaviors.
  3. no looking back: it is not possible to use ‘old’ references or capabilities in RDP. (I originally called this the ‘amnestic agent’ property, but the new phrase is better.) In a logical sense, you can understand RDP as ‘continuously’ publishing the new capability while revoking the one you were using an instant ago, with infinite precision. This has many valuable consequences:
    • security is simplified. Revocation only ever requires you stop sharing, and we don’t need to worry about ‘grandfathered’ capabilities using deprecated objects
    • distributed garbage collection is unnecessary. Each individual host must, of course, garbage collect signal values. But they never need to coordinate with other hosts to collect references.
    • mostly-transparent distribution is feasible. No special handling is needed for shared capabilities – they’re simply modeled as revoked during disruption.
    • upgrade and change are easier to reason about. I.e. no risk of aliasing an old behavior then failing to change behaviors after it is invalidated.

The distributed implementation of the ‘no looking back’ property is actually quite inexpensive. Locally, it just requires static analysis to prevent developers from storing behavior references into state. For the distributed system, it means either ‘transitioning’ a capability internally (i.e. so the same cap refers to the new dynamic behavior), or simply revoking a UUID/URL and distributing a new one after cutting off a participant. RDP’s reactive structure means we always know where to send the updated UUID. (The local implementation is even easier, via the type system and inability to statefully store dynamic behaviors.)

Anyhow, each constraint has partial workarounds. Static delay between demand and response can be achieved by executing for side-effects rather than observing the response, or by using a static behavior when delay is too bad. Continuous behaviors can be discretely updated. Agents can remember scripts (strings or structures that can be locally compiled into dynamic, tagless behavior) rather than share dynamic behaviors directly.

In some cases we’d want scripts anyway because they’re easy to audit, isolate, confine, and a DSL can apply domain-specific constraints. Remote capabilities can be translated into unguessable URLs proxied by a local service. This sort of ‘script sharing’ is how automated code distribution, sharding, and mobile agents will be modeled under-the-hood of my eventual language runtime.

These constraints and their workarounds are inconvenient. They hinder composition and generic programming of stateful systems. The constraints keep dynamic behavior off the path of least resistance. But that is not a bad thing: it is easier to reason about a static system, easier to implement efficiently and optimize, and under principle of least power, developers should be systematically discouraged from using dynamic behaviors where they don’t truly require them.

Modeling Dynamic Behavior

We construct and compose dynamic behaviors the same way we do static ones – dynamic behavior is just staged static behavior. But eventually we must execute or evaluate the dynamic behavior.

exec :: (DiscreteSignal s) => a (s (a d r_) :&: d) (s ())

eval :: (DiscreteSignal s) => a d r -> a (s (a d r) :&: d) r

First, we can execute a dynamic behavior. This allows a static RDP program to construct and control an arbitrary dynamic behavior so long as the response is dropped. Any feedback must occurs through RDP’s constrained side-effects, i.e. by manipulating an agent that the static behavior is observing. There is no delay – the response from ‘exec’ merely shadows the dynamic agent signal.

Second, we can evaluate dynamic behavior. This allows a static RDP program to embed a dynamic behavior into its workflow, using the response back in the static program. The first argument to ‘eval’ is a static behavior, which sets the static delay – whenever the demanded dynamic behavior would have higher delay, ‘eval’ will instead use the static behavior (which may, in turn, try to ‘eval’ from secondary fallback sources).

It may be possible to implement exec in terms of eval, though I’m not sure what to provide as the first argument to ‘eval’ in that case.

Stability of Dynamic Behaviors

RDP is designed with the assumption of a relatively stable behavior. There are overhead costs for link set-up and tear-down, so performance will suffer if changes in dynamic behavior occur too frequently. Fortunately, rapid switching is tolerable from a semantics perspective, will not cause glitches or inconsistencies, and will not be observable unless the switching rate outgrows the CPU and network capacity to keep up. This is primarily a performance concern.

Ultimately, it is up to developers to choose relatively stable bases for dynamic behavior, or at least stage them properly. This is the same chore developers have in any paradigm – decide what is ‘hard coded’ versus what is scripted or configured. Dynamic behaviors simply make it easier to do this staging within a common language and security model.

This entry was posted in Distributed Programming, Language Design, Open Systems Programming, Reactive Demand Programming. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s