Interconfection
1 Interconfection concepts
1.1 Quasi-determinism
1.2 Order-invariance
1.3 Purity
2 Order
2.1 Orderings
ordering-lt
ordering-lt?
ordering-eq
ordering-eq?
ordering-private
ordering-private?
ordering-gt
ordering-gt?
dex-result?
cline-result?
2.2 Names, Dexes, and Dexed Values
name?
dex?
getfx-is-in-dex
getfx-name-of
getfx-dexed-of
getfx-compare-by-dex
dexed?
dexed/  c
dexed-first-order/  c
dexed-get-dex
dexed-get-name
dexed-get-value
dex-name
dex-dex
dex-dexed
dex-give-up
dex-default
dex-opaque
dex-by-own-method
dex-fix
dex-tuple-by-field-position
dex-tuple
2.3 Clines
cline?
get-dex-from-cline
getfx-is-in-cline
getfx-compare-by-cline
dex-cline
cline-by-dex
cline-give-up
cline-default
cline-opaque
cline-by-own-method
cline-fix
cline-tuple-by-field-position
cline-tuple
cline-flip
2.4 Merges and Fuses
merge?
fuse?
getfx-call-merge
getfx-call-fuse
dex-merge
dex-fuse
merge-by-dex
merge-by-cline-min
merge-by-cline-max
fuse-by-merge
merge-opaque
fuse-opaque
merge-by-own-method
fuse-by-own-method
merge-fix
fuse-fix
merge-tuple-by-field-position
fuse-tuple-by-field-position
merge-tuple
fuse-tuple
2.5 Tables
table?
table-empty?
table-get
table-empty
table-shadow
getfx-table-map-fuse
getfx-table-sort
dex-table
dex-table-ordered
cline-table-ordered
merge-table
fuse-table
2.6 Fusable Functions
fusable-function?
make-fusable-function
fuse-fusable-function
2.7 Contracts for tables
table-kv-of
table-v-of
2.8 Operations for Other Data Types and Derived Operations
dex-trivial
dex-boolean
cline-boolean-by-truer
cline-boolean-by-falser
merge-boolean-by-and
merge-boolean-by-or
dex-immutable-string
cline-immutable-string
dex-exact-rational
cline-exact-rational
fuse-exact-rational-by-plus
fuse-exact-rational-by-times
assocs->table-if-mutually-unique
getfx-is-eq-by-dex
table-kv-map
table-kv-all?
table-kv-any?
table-v-map
table-v-all?
table-v-any?
3 Extensibility
3.1 Read-only extensibility effects ("getfx")
getfx?
getfx/  c
pure-run-getfx
getfx-done
getfx-bind
fuse-getfx
7.8

Interconfection

Interconfection is a library for building extensible systems, especially module systems. Interconfection extensions cooperate using a kind of quasi-deterministic concurrency, reflecting the reality of a cultural context where authors have developed and published their extensions without staying in perfect lockstep with each other’s work. Interconfection’s concurrency is an expressive solution to module system design concerns having to do with closed-world and open-world extensibility, including the Expression Problem.

Since the extensions of an Interconfection system are considered to be separate processes that communicate, there is a sense in which Interconfection is all about side effects. However, Interconfection’s side effects and Racket’s side effects work at cross purposes. Careless use of Racket side effects can break some of the encapsulation Interconfection relies upon to enforce determinism. Hence, Interconfection is most useful for Racket programs written in a certain purely functional style. Interconfection’s own side effects are expressed using techniques like monads so that they play well with that pure functional style.

    1 Interconfection concepts

      1.1 Quasi-determinism

      1.2 Order-invariance

      1.3 Purity

    2 Order

      2.1 Orderings

      2.2 Names, Dexes, and Dexed Values

      2.3 Clines

      2.4 Merges and Fuses

      2.5 Tables

      2.6 Fusable Functions

      2.7 Contracts for tables

      2.8 Operations for Other Data Types and Derived Operations

    3 Extensibility

      3.1 Read-only extensibility effects ("getfx")

1 Interconfection concepts

1.1 Quasi-determinism

Quasi-determinism is a notion explored in "Freeze After Writing: Quasi-Deterministic Parallel Programming with LVars." No two runs of the same quasi-deterministic computation return different values, but some runs are allowed not to return a value because they encounter synchronization errors, out-of-memory conditions, nontermination, or other detours.

For many realistic systems, quasi-determinism is semantically justified by the fact that the program can’t determine for itself whether the user will pull the plug on it, and there’s nothing it can do in that situation to return the correct result.

For the purposes of Interconfection, we actually use a somewhat stricter form of quasi-determinism: Yes our processes can shirk determinism if they encounter errors or resource exhaustion, but we still consider the fact that an error occurred to be part of the program’s result. That is, if there are sufficient resources to run an Interconfection program and at least one error occurs, then at least one error is bound to occur no matter how many times we try to run it. (It’s possible for it to be a different set of errors each time.)

Because of this slightly stronger determinism, Interconfection can’t make much use of the LVar approach’s "freeze after writing" technique. Using that technique, one process freezes a state resource so that later writes to that resource cause errors. In Interconfection, such a "freeze" can’t occur until after we manage to guarentee there will be no more write attempts, at which point it doesn’t accomplish much anyway.

Nevertheless, a similar effect can be achieved in Interconfection using a subsystem that’s been designed for closed-world extensibility. Using a system of "tickets," which essentially maintain a reference count to the shared resource, it’s possible to detect when there are no tickets remaining, at which point the complete set of writes is known and can be passed along to further computations that need to use it.

1.2 Order-invariance

The topic of concurrency might call to mind race conditions, but there are no race conditions as long as the order of effects doesn’t matter. When those effects accumulate contributions to a set, then it’s important that the computation that acts on that complete set can’t detect what order the contributions were made in.

For that reason, many of the exports of Interconfection are operations we call clines, dexes, merges, and fuses, specifically designed for order-invariant manipulation of sets of values.

1.3 Purity

Unfortunately, the arbitrary use of Racket side effects can record the order that the Racket code runs in, which can break the order-invariance of Interconfection’s abstractions and thereby create true race conditions. Interconfection is designed for use in a more purely functional Racket programming style.

There are certain operations we’re committed to calling "pure," in particular raise, lambda, and #%app (the function call operation). This is true even though lambda and #%app can risk out-of-memory errors and nontermination, even though a lambda expression can allocate a new value each time it’s called, and even though raise and #%app (with the wrong arity) can cause run time errors.

Generally speaking, if a Racket operation obeys Interconfection’s notion of quasi-determinism, has no external or internal side effects, and cannot be used to detect an impurity in raise, lambda, or #%app, then we consider it pure.

Of course, we consider operations like set!, parameterize, and the invocation of first-class continuations to be impure because they break referential transparency: They make it so the results of two identical expressions in the same lexical scope can be two completely different values. And we consider calls to procedures like display to be impure even though they always have the same return value, since having two identical display expressions in the same lexical scope has a different effect than having just one and reusing its result.

For Interconfection’s purposes, we consider certain things like eq?, and anything like equal? that depends on them, to be impure operations. That’s because they could otherwise detect an impurity in lambda. By taking this point of view, many other operations can be considered pure, like list and append. An impure program can distinguish the results of (list 1 2) and (list 1 2) using eq?, but a pure program finds them to be indistinguishable.

We consider it impure to catch an exception without (either exhausting resources or) raising an exception to replace it. By catching exceptions, a Racket program can detect which of two subcomputations was attempted first, which directly defeats the order invariance Interconfection’s abstractions establish and depend on for their quasi-determinism.

There may be a few more subtleties than this.

There are cases where a Racket program may have to resort to impurity at the edges even if it makes use of Interconfection in regions of relative purity. For instance, the struct operation itself is impure since it generates a new structure type identity each time it’s used, but Racket programs don’t have a lot of other options for coining user-defined, encapsulated data types. Similarly, quite a number of essential operations in Racket’s macro system are impure. As long as the functions passed to Interconfection operations are pure, these other impure techniques should be fine.

2 Order

 (require interconfection/order/base)
  package: interconfection-lib

A cline is based on a total ordering on values in its domain, or in other words a binary relation that is reflexive, transitive, and antisymmetric. Its antisymmetry is as fine-grained as possible: If any two values in a cline’s domain are related by that cline in both directions, only impure code will be able to distinguish the two values.

However, a cline does not merely expose this total ordering. Within the cline’s domain, there may be equivalence classes of values for which every two nonequal values will not have their relative order exposed to pure code. When pure code uses getfx-compare-by-cline to compare two values by a cline, it can get several results:

A dex is like a cline, but it never results in the “candidly precedes” and “candidly follows” cases. Thus, a dex is useful as a kind of equality test.

All the exports of interconfection/order/base are also exported by interconfection/order.

2.1 Orderings

syntax

ordering-lt

syntax

(ordering-lt)

match expander

(ordering-lt)

procedure

(ordering-lt? v)  boolean?

  v : any/c
Struct-like operations which construct and deconstruct a value that represents the result of a comparison where the first value turned out to be candidly strictly less than the second value.

For the purposes of impure Racket code, every two ordering-lt values are equal?.

syntax

ordering-eq

syntax

(ordering-eq)

match expander

(ordering-eq)

procedure

(ordering-eq? v)  boolean?

  v : any/c
Struct-like operations which construct and deconstruct a value that represents the result of a comparison where the first value turned out to be equal to the second value.

For the purposes of impure Racket code, every two ordering-eq values are equal?.

syntax

ordering-private

syntax

(ordering-private)

match expander

(ordering-private)

procedure

(ordering-private? v)  boolean?

  v : any/c
Struct-like operations which construct and deconstruct a value that represents the result of a comparison where the first value turned out to be secretly strictly less than or secretly strictly greater than the second value.

For the purposes of impure Racket code, every two ordering-private values are equal?.

syntax

ordering-gt

syntax

(ordering-gt)

match expander

(ordering-gt)

procedure

(ordering-gt? v)  boolean?

  v : any/c
Struct-like operations which construct and deconstruct a value that represents the result of a comparison where the first value turned out to be candidly strictly greater than the second value.

For the purposes of impure Racket code, every two ordering-gt values are equal?.

procedure

(dex-result? x)  boolean?

  x : any/c
Returns whether the given value is a possible result for a dex (something that satisfies either ordering-eq? or ordering-private?).

procedure

(cline-result? x)  boolean?

  x : any/c
Returns whether the given value is a possible result for a dex (something that satisfies ordering-lt?, dex-result?, or ordering-gt?).

2.2 Names, Dexes, and Dexed Values

procedure

(name? x)  boolean?

  x : any/c
Returns whether the given value is a name. In Interconfection, a name is something like a partial application of comparison by a dex. Any value can be converted to a name using getfx-name-of if any dex for that value is at hand (and it always converts to the same name regardless of which dex is chosen), and names themselves can be compared using (dex-name).

procedure

(dex? x)  boolean?

  x : any/c
Returns whether the given value is a dex.

procedure

(getfx-is-in-dex dex x)  (getfx/c boolean?)

  dex : dex?
  x : any/c
Given a dex and a value, returns a getfx? computation that computes whether the value belongs to the dex’s domain.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given dex. This is one way to "call" a dex.

procedure

(getfx-name-of dex x)  (getfx/c (maybe/c name?))

  dex : dex?
  x : any/c
Given a dex and a value, returns a getfx? computation. If the value belongs to the dex’s domain, this computation results in a just of a name that the value can be compared by. Otherwise, it results in a nothing.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given dex. This is one way to "call" a dex.

procedure

(getfx-dexed-of dex x)  (getfx/c (maybe/c dexed?))

  dex : dex?
  x : any/c
Given a dex and a value, returns a just of a dexed version of the given value, if the value belongs to the dex’s domain; otherwise returns a nothing. Given a dex and a value, returns a getfx? computation. If the value belongs to the dex’s domain, this computation results in a just of a dexed version of the given value. Otherwise, it results in a nothing.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given dex. This is one way to "call" a dex.

procedure

(getfx-compare-by-dex dex a b)  (getfx/c (maybe/c dex-result?))

  dex : dex?
  a : any/c
  b : any/c
Given a dex and two values, returns a getfx? computation that compares those values according to the dex. The result is (nothing) if either value is outside the dex’s domain.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given dex. This is one way to "call" a dex.

procedure

(dexed? x)  boolean?

  x : any/c
Returns whether the given value is a dexed value. In Interconfection, a dexed value is a container that carries a value, its name, and a dex that recognizes only that value. Dexed values and names are obtained in similar ways (getfx-dexed-of and getfx-name-of) and both serve the purpose of being a value that can identify itself, but they differ in transparency: Dexed values allow the original value to be retrieved, whereas names can do nothing but be compared to other names.

procedure

(dexed/c c)  contract?

  c : contract?
Returns a contract that recognizes a dexed value and additionally imposes the given contract on its dexed-get-value. That contract’s projection must be ordering-eq to the original value. This essentially means the contract must be first-order.

procedure

(dexed-first-order/c c)  contract?

  c : contract?
Returns a contract that recognizes a dexed value and additionally imposes the first-order behavior of the given contract on its dexed-get-value. It ignores the contract’s higher-order behavior altgoether, so using certain contracts with dexed-first-order/c has little purpose other than documentation value.

This is nearly the same as (dexed/c (contract-first-order c)), but its contract-name is based on that of c.

procedure

(dexed-get-dex d)  dex?

  d : dexed?
Given a dexed value, returns a dex that has a domain consisting of just one value, namely the value of the given dexed value.

A call to the resulting dex can be run through pure-run-getfx without problems.

When compared by (dex-dex), all dexed-get-dex results are ordering-eq if the corresponding dexed-get-value results are.

procedure

(dexed-get-name d)  name?

  d : dexed?
Given a dexed value, returns the name of its value.

procedure

(dexed-get-value d)  any/c

  d : dexed?
Given a dexed value, returns its value.

procedure

(dex-name)  dex?

Returns a dex that compares names.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(dex-dex)  dex?

Returns a dex that compares dexes.

All presently existing dexes allow this comparison to be fine-grained enough that it trivializes their equational theory. For instance, (dex-default (dex-give-up) (dex-give-up)) and (dex-give-up) can be distinguished this way despite otherwise having equivalent behavior.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(dex-dexed)  dex?

Returns a dex that compares dexed values.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(dex-give-up)  dex?

Returns a dex over an empty domain.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(dex-default dex-for-trying-first    
  dex-for-trying-second)  dex?
  dex-for-trying-first : dex?
  dex-for-trying-second : dex?
Given two dexes, returns a dex over the union of their domains.

For the sake of nontermination, error, and performance concerns, this attempts to compute the result using dex-for-trying-first before it moves on to dex-for-trying-second.

The invocation of dex-for-trying-second is a tail call.

If calls to the given dexes can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), all dex-default values are ordering-eq if their dex-for-trying-first values are and their dex-for-trying-second values are.

procedure

(dex-opaque name dex)  dex?

  name : name?
  dex : dex?
Given a name and a dex, returns another dex that behaves like the given one but is not equal to it.

If calls to the given dex can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), all dex-opaque values are ordering-eq if their name values are and their dex values are.

procedure

(dex-by-own-method dexed-getfx-get-method)  dex?

  dexed-getfx-get-method : (dexed-first-order/c (-> any/c (getfx/c (maybe/c dex?))))
Given a dexed getfx? operation, returns a dex that works like so:

  1. It invokes the getfx? operation with each value. If any of these invocations results in (nothing), those values are not considered to be in this dex’s domain, and the overall result is (nothing). Otherwise, the computation proceeds:

  2. It checks that the dex methods obtained this way are all ordering-eq. If they’re not, the values are evidently distinguishable, and the overall result is (just (ordering-private)). Otherwise, the computation proceeds:

  3. It tail-calls the method.

If the getfx? computations that result from dexed-getfx-get-method and the calls to their resulting dexes can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), all dex-by-own-method values are ordering-eq if their dexed-getfx-get-method values are.

procedure

(dex-fix dexed-getfx-unwrap)  dex?

  dexed-getfx-unwrap : (dexed-first-order/c (-> dex? (getfx/c dex?)))
Given a dexed getfx? operation, returns a dex that works by passing itself to the operation and then tail-calling the resulting dex.

If the getfx? computations that result from dexed-getfx-unwrap and the calls to their resulting dexes can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), all dex-fix values are ordering-eq if their dexed-getfx-unwrap values are.

syntax

(dex-tuple-by-field-position tupler-expr
  [field-position-nat dex-expr]
  ...)
 
  tupler-expr : (tupler/c (=/c (length '(dex-expr ...))))
  dex-expr : dex?
Returns a dex that compares instances of the given tupler if their field values can be compared by the dexes produced by the dex-expr expressions.

Each field-position-nat must be a distinct number indicating which field should be checked by the associated dex, and there must be an entry for every field.

For the sake of nontermination, error, and performance concerns, this dex computes by attempting the given dexes in the order they appear in this call. If a dex before the last one determines a non-ordering-eq result, the following dexes are only checked to be sure their domains contain the respective field values. Otherwise, the last dex, if any, is attempted as a tail call.

If calls to the given dexes can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), all dex-tuple-by-field-position values are ordering-eq if they’re for the same tupler, if they have field-position-nat values in the same sequence, and if their dex-expr values are ordering-eq.

syntax

(dex-tuple tupler-expr dex-expr ...)

 
  tupler-expr : (tupler/c (=/c (length '(dex-expr ...))))
  dex-expr : dex?
Returns a dex that compares instances of the given tupler if their field values can be compared by the dexes produced by the dex-expr expressions.

For the sake of nontermination, error, and performance concerns, this dex computes by attempting the given dexes in the order they appear in this call. The last dex, if any, is attempted as a tail call.

If calls to the given dexes can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), each dex-tuple value is ordering-eq to the equivalent dex-tuple-by-field-position value.

2.3 Clines

procedure

(cline? x)  boolean?

  x : any/c
Returns whether the given value is a cline.

procedure

(get-dex-from-cline cline)  dex?

  cline : cline?
Given a cline, returns a dex over the same domain.

If calls to the given cline can be run through pure-run-getfx without problems, then so can a call to the resulting dex.

procedure

(getfx-is-in-cline cline x)  (getfx/c boolean?)

  cline : cline?
  x : any/c
Given a cline and a value, returns a getfx? computation that computes whether the value belongs to the cline’s domain.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given cline. This is one way to "call" a cline.

procedure

(getfx-compare-by-cline cline a b)

  (getfx/c (maybe/c cline-result?))
  cline : cline?
  a : any/c
  b : any/c
Given a cline and two values, returns a getfx? computation that compares those values according to the cline. The result is (nothing) if either value is outside the cline’s domain.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given cline. This is one way to "call" a cline.

procedure

(dex-cline)  dex?

Returns a dex that compares clines.

Almost all presently existing clines allow this comparison to be fine-grained enough that it trivializes their equational theory. For instance, (cline-default (cline-give-up) (cline-give-up)) and (cline-give-up) can be distinguished this way despite otherwise having equivalent behavior. One exception is that calling cline-flip twice in a row results in a cline that’s ordering-eq to the original.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(cline-by-dex dex)  cline?

  dex : dex?
Returns a cline that compares values by tail-calling the given dex. Since the dex never returns the "candidly precedes" or "candidly follows" results, this cline doesn’t either.

If calls to the given dex can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), all cline-by-dex values are ordering-eq if their dexes are.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to the original dex.

procedure

(cline-give-up)  cline?

Returns a cline over an empty domain.

A call to this cline can be run through pure-run-getfx without problems.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to (dex-give-up).

procedure

(cline-default cline-for-trying-first    
  cline-for-trying-second)  cline?
  cline-for-trying-first : cline?
  cline-for-trying-second : cline?
Given two clines, returns a cline over the union of their domains. The resulting cline’s ascending order consists of the first cline’s ascending order in its domain, followed by the second cline’s ascending order outside the first cline’s domain.

For the sake of nontermination, error, and performance concerns, this attempts to compute the result using cline-for-trying-first before it moves on to cline-for-trying-second.

The invocation of cline-for-trying-second is a tail call.

If calls to the given clines can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), all cline-default values are ordering-eq if their cline-for-trying-first values are and their cline-for-trying-second values are.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to the similarly constructed dex-default.

procedure

(cline-opaque name cline)  cline?

  name : name?
  cline : cline?
Given a name and a cline, returns another cline that behaves like the given one but is not equal to it.

If calls to the given cline can be run through pure-run-getfx without problems, then so can a call to the resulting cline.

When compared by (dex-cline), all cline-opaque values are ordering-eq if their name values are and their cline values are.

procedure

(cline-by-own-method dexed-getfx-get-method)  cline?

  dexed-getfx-get-method : (dexed-first-order/c (-> any/c (getfx/c (maybe/c cline?))))
Given a dexed getfx? operation, returns a cline that works like so:

  1. It invokes the getfx? operation with each value. If any of these invocations results in (nothing), those values are not considered to be in this cline’s domain, and the overall result is (nothing). Otherwise, the computation proceeds:

  2. It checks that the cline methods obtained this way are all ordering-eq. If they’re not, it raises an error. Otherwise, the computation proceeds:

  3. It tail-calls the method.

If the getfx? computations that result from dexed-getfx-get-method and the calls to their resulting clines can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), all cline-by-own-method values are ordering-eq if their dexed-getfx-get-method values are.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to another dex only if that dex was obtained the same way from a cline ordering-eq to this one.

procedure

(cline-fix dexed-getfx-unwrap)  cline?

  dexed-getfx-unwrap : (dexed-first-order/c (-> cline? (getfx/c cline?)))
Given a dexed getfx? operation, returns a cline that works by passing itself to the operation and then tail-calling the resulting cline.

If the getfx? computations that result from dexed-getfx-unwrap and the calls to their resulting clines can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), all cline-fix values are ordering-eq if their dexed-getfx-unwrap values are.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to another dex only if that dex was obtained the same way from a cline ordering-eq to this one.

syntax

(cline-tuple-by-field-position tupler-expr
  [field-position-nat cline-expr]
  ...)
 
  tupler-expr : (tupler/c (=/c (length '(cline-expr ...))))
  cline-expr : cline?
Returns a cline that compares instances of the given tupler if their field values can be compared by the clines produced by the cline-expr expressions. The comparison is lexicographic, with the most significant comparisons being the cline-expr values that appear earliest in this call.

Each field-position-nat must be a distinct number indicating which field should be checked by the associated cline, and there must be an entry for every field.

For the sake of nontermination, error, and performance concerns, this cline computes by attempting the given clines in the order they appear in this call. If a cline before the last one determines a non-ordering-eq result, the following clines are only checked to be sure their domains contain the respective field values. Otherwise, the last cline, if any, is attempted as a tail call.

If calls to the given clines can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), all cline-tuple-by-field-position values are ordering-eq if they’re for the same tupler, if they have field-position-nat values in the same sequence, and if their cline-expr values are ordering-eq.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to the similarly constructed dex-tuple-by-field-position.

syntax

(cline-tuple tupler-expr cline-expr ...)

 
  tupler-expr : (tupler/c (=/c (length '(cline-expr ...))))
  cline-expr : cline?
Returns a cline that compares instances of the given tupler if their field values can be compared by the clines produced by the cline-expr expressions. The comparison is lexicographic, with the most significant comparisons being the cline-expr values that appear earliest in this call.

For the sake of nontermination, error, and performance concerns, this cline computes by attempting the given clines in the order they appear in this call. If a cline before the last one determines a non-ordering-eq result, the following clines are only checked to be sure their domains contain the respective field values. Otherwise, the last cline, if any, is attempted as a tail call.

If calls to the given clines can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), each cline-tuple value is ordering-eq to the equivalent cline-tuple-by-field-position value.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to the similarly constructed dex-tuple.

procedure

(cline-flip cline)  cline?

  cline : cline?
Returns a cline that compares values by calling the given dex but reverses the "candidly precedes" and "candidly follows" results (ordering-lt and ordering-gt). It dosn’t reverse the "secretly precedes" and "secretly follows" results.

If calls to the given cline can be run through pure-run-getfx without problems, then so can a call to the resulting cline.

When compared by (dex-cline), cline-flip values are usually ordering-eq if their given clines are. The one exception is that calling cline-flip twice in a row has no effect; the result of the second call is ordering-eq to the original cline. This behavior is experimental; future revisions to this library may remove this exception or add more exceptions (such as having (cline-flip (cline-default a b)) be ordering-eq to (cline-default (cline-flip a) (cline-flip b))).

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to the dex obtained the same way from the original cline.

2.4 Merges and Fuses

Interconfection offers a non-exhaustive but extensive selection of merges and fuses. These are values which can be compared for equality with like values (using (dex-merge) and (dex-fuse)), and they represent operations of two arguments (invocable using getfx-call-merge and getfx-call-fuse).

Merges represent operations that are commutative, associative, and idempotent, or in other words exactly the kind of operation that can operate on a (nonempty and finite) unordered set of inputs.

Fuses represent operations that are commutative and associative (and not necessarily idempotent). A fuse is ideal for operating on a (nonempty and finite) unordered multiset of inputs.

The idempotence of a merge operation is such that if the two inputs to the merge are ordering-eq by any dex, the result will be ordering-eq to them both by the same dex.

Calling a merge/fuse is a partial operation. A single merge/fuse is associated with certain domains of values it works on, and these domains are disjoint from each other. If it’s given a set/multiset of operands that are all elements of the same domain, it returns a just of another value in that domain. If it’s given a set/multiset of operands that don’t all belong to the same domain, it returns (nothing), even if each operand belongs to some domain it accepts.

procedure

(merge? x)  boolean?

  x : any/c

procedure

(fuse? x)  boolean?

  x : any/c
Returns whether the given value is a merge/fuse.

procedure

(getfx-call-merge merge a b)  (getfx/c maybe?)

  merge : merge?
  a : any/c
  b : any/c

procedure

(getfx-call-fuse fuse a b)  (getfx/c maybe?)

  fuse : fuse?
  a : any/c
  b : any/c
Given a merge/fuse and two values, this getfx? computation combines those values according to the merge/fuse. The result is (nothing) if the two values don’t both belong to the same domain of the merge/fuse. Otherwise, the result is (just value) for some value that also belongs to that domain.

Whether this getfx? computation can be run through pure-run-getfx without problems depends on the given merge/fuse.

For getfx-call-merge, if there is any dex for which the input values are ordering-eq, then the result will be ordering-eq to them both.

procedure

(dex-merge)  dex?

procedure

(dex-fuse)  dex?

Returns a dex that compares merges/fuses.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(merge-by-dex dex)  merge?

  dex : dex?
Returns a merge that merges any values that are already ordering-eq according the given dex. The result of the merge is ordering-eq to both of the inputs.

Note that this tends to be a merge with many domains, one domain for each value accepted by the given dex. In other words, two values that are each accepted by the given dex but which aren’t ordering-eq will not belong to the same domain of this merge, so the result of getfx-call-merge on those values will be (nothing).

If calls to the given dex can be run through pure-run-getfx without problems, then so can a call to this merge.

When compared by (dex-merge), all merge-by-dex values are ordering-eq if their dexes are.

procedure

(merge-by-cline-min cline)  merge?

  cline : cline?
Returns a merge that finds the minimum of any set of values in the given cline’s domain. The result of the merge is ordering-eq to at least one of the inputs, and it’s ordering-lt to the rest.

If calls to the given cline can be run through pure-run-getfx without problems, then so can a call to this merge.

When compared by (dex-merge), all merge-by-cline-min values are ordering-eq if their clines are. They’re also ordering-eq to (merge-by-cline-max (cline-flip cline)).

procedure

(merge-by-cline-max cline)  merge?

  cline : cline?
Returns a merge that finds the maximum of any set of values in the given cline’s domain. The result of the merge is ordering-eq to at least one of the inputs, and it’s ordering-gt to the rest.

If calls to the given cline can be run through pure-run-getfx without problems, then so can a call to this merge.

When compared by (dex-merge), all merge-by-cline-max values are ordering-eq if their clines are. They’re also ordering-eq to (merge-by-cline-min (cline-flip cline)).

procedure

(fuse-by-merge merge)  fuse?

  merge : merge?
Returns a fuse that fuses values by merging them using the given merge.

If calls to the given merge can be run through pure-run-getfx without problems, then so can a call to the resulting fuse.

When compared by (dex-fuse), all fuse-by-merge values are ordering-eq if their merges are.

procedure

(merge-opaque name merge)  merge?

  name : name?
  merge : merge?

procedure

(fuse-opaque name fuse)  fuse?

  name : name?
  fuse : fuse?
Given a name and a merge/fuse, returns another merge/fuse that behaves like the given one but is not equal to it.

If calls to the given merge/fuse can be run through pure-run-getfx without problems, then so can a call to the resulting merge/fuse.

When compared by (dex-merge)/(dex-fuse), all merge-opaque/fuse-opaque values are ordering-eq if their name values are and their merge/fuse values are.

procedure

(merge-by-own-method dexed-getfx-get-method)  merge?

  dexed-getfx-get-method : (dexed-first-order/c (-> any/c (getfx/c (maybe/c merge?))))

procedure

(fuse-by-own-method dexed-getfx-get-method)  fuse?

  dexed-getfx-get-method : (dexed-first-order/c (-> any/c (getfx/c (maybe/c fuse?))))
Given a dexed getfx? operation, returns a merge/fuse that works like so:

  1. It invokes the getfx? operation with each value. If any of these invocations results in (nothing), those values are not considered to be in any of this merge’s/fuse’s domains, and the overall result is (nothing). Otherwise, the computation proceeds:

  2. It checks that the merge/fuse methods obtained this way are all ordering-eq. If they’re not, the values are considered to be in multiple disjoint domains, and the overall result is (nothing). Otherwise, the computation proceeds:

  3. It invokes the method. If the result of that invocation is (nothing), the overall result is (nothing). Otherwise, the result of the invocation is of the form (just result), and the computation proceeds:

  4. To ensure that the overall merge/fuse is associative, it invokes the method-getting getfx? operation one more time on result. If it does not obtain a merge/fuse method that’s ordering-eq to the one originally obtained from the inputs, it raises an error. Otherwise, the computation proceeds:

  5. The overall result is (just result).

If the getfx? computations that result from dexed-getfx-get-method and the calls to their resulting merges/fuses can be run through pure-run-getfx without problems, then so can a call to this merge/fuse.

When compared by (dex-merge)/(dex-fuse), all merge-by-own-method/fuse-by-own-method values are ordering-eq if their dexed-get-method values are.

procedure

(merge-fix dexed-getfx-unwrap)  merge?

  dexed-getfx-unwrap : (dexed-first-order/c (-> merge? (getfx/c merge?)))

procedure

(fuse-fix dexed-getfx-unwrap)  fuse?

  dexed-getfx-unwrap : (dexed-first-order/c (-> fuse? (getfx/c fuse?)))
Given a dexed getfx? operation, returns a merge/fuse that works by passing itself to the operation and then tail-calling the resulting merge/fuse.

If the getfx? computations that result from dexed-getfx-unwrap and the calls to their resulting merges/fuses can be run through pure-run-getfx without problems, then so can a call to this merge/fuse.

When compared by (dex-merge)/(dex-fuse), all merge-fix/fuse-fix values are ordering-eq if their dexed-getfx-unwrap values are.

syntax

(merge-tuple-by-field-position tupler-expr
  [field-position-nat field-method-expr]
  ...)
 
  tupler-expr : (tupler/c (=/c (length '(field-method-expr ...))))
  field-method-expr : merge?

syntax

(fuse-tuple-by-field-position tupler-expr
  [field-position-nat field-method-expr]
  ...)
 
  tupler-expr : (tupler/c (=/c (length '(field-method-expr ...))))
  field-method-expr : fuse?
Returns a merge/fuse that combines instances of the given tupler if their field values can be combined by the merges/fuses produced by the field-method-expr expressions.

Each field-position-nat must be a distinct number indicating which field should be checked by the associated merge/fuse, and there must be an entry for every field.

If calls to the given merges/fuses can be run through pure-run-getfx without problems, then so can a call to this merge/fuse.

When compared by (dex-merge)/(dex-fuse), all merge-tuple-by-field-position/fuse-tuple-by-field-position values are ordering-eq if they’re for the same tupler, if they have field-position-nat values in the same sequence, and if their field-method-expr values are ordering-eq.

syntax

(merge-tuple tupler-expr field-method-expr ...)

 
  tupler-expr : (tupler/c (=/c (length '(field-method-expr ...))))
  field-method-expr : merge?

syntax

(fuse-tuple tupler-expr field-method-expr ...)

 
  tupler-expr : (tupler/c (=/c (length '(field-method-expr ...))))
  field-method-expr : fuse?
Returns a merge/fuse that combines instances of the given tupler if their field values can be combined by the merges/fuses produced by the field-method-expr expressions.

If calls to the given merges/fuses can be run through pure-run-getfx without problems, then so can a call to this merge/fuse.

When compared by (dex-merge)/(dex-fuse), each merge-tuple/fuse-tuple value is ordering-eq to the equivalent merge-tuple-by-field-position/fuse-tuple-by-field-position value.

2.5 Tables

Interconfection’s tables are similar to Racket hash tables where all the keys are Interconfection dexed values. However, tables are encapsulated in such a way that pure code will always process the table entries in an order-oblivious way. For instance, an Interconfection table cannot be converted to a list in general. This makes tables useful for representing orderless sets that cross API boundaries, where the API client should not be able to depend on accidental details of the set representation.

procedure

(table? x)  boolean?

  x : any/c
Returns whether the given value is an Interconfection table.

procedure

(table-empty? x)  boolean?

  x : table?
Returns whether the given table is empty.

procedure

(table-get key table)  maybe?

  key : dexed?
  table : table?
Returns the value associated with the given key in the given table, if any.

procedure

(table-empty)  table?

Returns an empty table.

procedure

(table-shadow key maybe-val table)  table?

  key : dexed?
  maybe-val : maybe?
  table : table?
Returns another table that’s just like the given one, except that the table-get result for the given key is the given maybe? value. That is, this overwrites or removes the value associated with the given key.

procedure

(getfx-table-map-fuse table    
  fuse    
  key-to-operand)  (getfx/c maybe?)
  table : table?
  fuse : fuse?
  key-to-operand : (-> dexed? getfx?)
Given a table, a fuse, and a getfx? operation, returns a getfx? computation that calls that operation with each key of the table and results in a just containing the fused value of all the operation’s results. If the table is empty or if any operation result is outside the fuse’s domain, this computation results in (nothing) instead.

If the getfx? computations that result from key-to-operand and calls to the given fuse can be run through pure-run-getfx without problems, then so can the overall computation.

procedure

(getfx-table-sort cline table)

  (getfx/c (maybe/c (listof table?)))
  cline : cline?
  table : table?
Given a cline and a table, returns a getfx? computation that sorts the values of the table by the cline, without determining an order on values that the cline doesn’t determine an order on. This computation results in (nothing) if any of the values are outside the cline’s domain. Otherwise, it results in a just containing a list of nonempty tables, partitioning the original table’s values in ascending order.

What we mean by partitioning is this: Each entry of the original table appears in one and only one table in the list, and the tables have no other entries.

What we mean by ascending order is this: If the given cline computes that one value of the original table is (ordering-lt) to a second value, then the two values are stored in two different tables, and the first value’s table precedes the second value’s table in the list. Likewise (and equivalently), if a value is (ordering-gt) to a second value, the first occurs after the second in the list of tables.

procedure

(dex-table dex-val)  dex?

  dex-val : dex?
Returns a dex that compares tables, using the given dex to compare each value.

If calls to the given dex can be run through pure-run-getfx without problems, then so can a call to the resulting dex.

When compared by (dex-dex), all dex-table values are ordering-eq if their dex-val values are.

procedure

(dex-table-ordered assoc)  dex?

  assoc : (listof (list/c dexed? dex?))
Returns a dex that compares tables that have precisely the given set of dexed values as keys and whose values can be compared by the corresponding dexes.

The given keys must be mutually unique.

For the sake of nontermination, error, and performance concerns, this dex computes by attempting the given dexes in the order they appear in the assoc association list. If a dex before the last one determines a non-ordering-eq result, the following dexes are only checked to be sure their domains contain the respective field values. Otherwise, the last dex, if any, is attempted as a tail call.

If calls to the given dexes can be run through pure-run-getfx without problems, then so can a call to this dex.

When compared by (dex-dex), all dex-table-ordered values are ordering-eq if they have the same keys in the same sequence and if the associated dexes are ordering-eq.

procedure

(cline-table-ordered assoc)  cline?

  assoc : (listof (list/c dexed? cline?))
Returns a cline that compares tables that have precisely the given set of dexed values as keys and whose values can be compared by the corresponding clines. The comparison is lexicographic, with the most significant comparisons being the clines that appear earliest in the assoc association list.

The given keys must be mutually unique.

For the sake of nontermination, error, and performance concerns, this cline computes by attempting the given clines in the order they appear in the assoc association list. If a cline before the last one determines a non-ordering-eq result, the following clines are only checked to be sure their domains contain the respective field values. Otherwise, the last cline, if any, is attempted as a tail call.

If calls to the given clines can be run through pure-run-getfx without problems, then so can a call to this cline.

When compared by (dex-cline), all cline-table-ordered values are ordering-eq if they have the same keys in the same sequence and if the associated clines are ordering-eq.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to the similarly constructed dex-table-ordered.

procedure

(merge-table merge-val)  merge?

  merge-val : merge?

procedure

(fuse-table fuse-val)  fuse?

  fuse-val : fuse?
Returns a merge/fuse that combines tables by collecting all the nonoverlapping entries and combining the overlapping entries using the given merge-val/fuse-val.

If calls to the given merge/fuse can be run through pure-run-getfx without problems, then so can a call to the resulting merge/fuse.

When compared by (dex-merge)/(dex-fuse), all merge-table/fuse-table values are ordering-eq if their merge-val/fuse-val values are.

2.6 Fusable Functions

The dex and cline utilities are good for early detection of equality on inductive information, information that we have access to all at once. For coinductive information – that which we may never see the end of – we cannot detect equality early. However, we may still do things based on an assumption of equality and then enforce this assumption as new information comes to light.

Interconfection uses a dedicated kind of encapsulated data, fusable functions, for this purpose. As the name implies, fusable functions support a fuse operation. This operation returns a new fusable function right away. Subsequent calls to that function work by calling each of the original functions and fusing their results – a computation which can cause errors if the return values turn out not to be as fusable as expected. We can use those errors to enforce our equality assumptions on the fly.

Interconfections’s dexes and clines can’t do this kind of delayed enforcement because they only compute simple values like (ordering-lt).

It’s arguable whether Interconfection’s merges could do this. The property that sets apart a merge from a fuse is that a merge must be idempotent; the result of merging a value with itself must be indistinguishable from the original value. When we fuse a fusable function with itself, we end up with a function that does at least double the amount of computation, so in practice, the original and the fusion will not be indistinguishable. Because of this, Interconfection’s fusable functions only come with a fuse operation, not a merge operation.

An Interconfection fusable-function? is also a procedure? value. It can be invoked just like any other Racket procedure.

There is currently no way to make a fusable function that performs a tail call. This property wouldn’t be preserved by fuse-fusable-function anyway.

procedure

(fusable-function? x)  boolean?

  x : any/c
Returns whether the given value is an Interconfection fusable function value.

procedure

(make-fusable-function proc)  fusable-function?

  proc : (-> any/c getfx?)
Returns a fusable function that behaves like the given single-input, single-output getfx? operation.

If the getfx? computations that result from proc can be run through pure-run-getfx without problems, then so can a call to the resulting fusable function.

procedure

(fuse-fusable-function dexed-getfx-arg-to-method)  fuse?

  dexed-getfx-arg-to-method : (dexed-first-order/c (-> any/c (getfx/c fuse?)))
Given dexed-getfx-arg-to-method as a dexed function, returns a fuse that combines fusable functions. The combined fusable function works by calling the dexed-getfx-arg-to-method function and running its getfx? result to get a fuse; doing the same with both of the original fusable functions to get each of their results; and fusing the results by that fuse. If the results turn out not to be in the fuse’s domain, this causes an error.

If the getfx? computations that result from dexed-getfx-arg-to-method and the calls to their resulting fuses can be run through pure-run-getfx without problems, then so can a call to the fused fusable function.

A call to this fuse can be run through pure-run-getfx without problems.

When compared by (dex-dex), all fuse-fusable-function values are ordering-eq if their dexed-getfx-arg-to-method values are.

2.7 Contracts for tables

procedure

(table-kv-of unwrapped-k/c v/c)  contract?

  unwrapped-k/c : contract?
  v/c : contract?
Returns a contract that recognizes a table? where the keys are constrained in a certain way by the contract unwrapped-k/c and the mapped values obey the contract v/c.

Specifically, since the keys of a table are always dexed values, the contract unwrapped-k/c on the keys applies to the unwrapped values of the keys, rather than the keys themselves.

The unwrapped-k/c contract’s projection on each unwrapped key must be ordering-eq to the original unwrapped key. This essentially means the contract must be first-order.

procedure

(table-v-of c)  contract?

  c : contract?
Returns a contract that recognizes a table? where the mapped values obey the given contract.

2.8 Operations for Other Data Types and Derived Operations

 (require interconfection/order)
  package: interconfection-lib

The interconfection/order module exports all the definitions of interconfection/order/base plus the definitions below.

procedure

(dex-trivial)  dex?

Returns a dex that compares trivial? values from Lathe Comforts. Every two trivial? values are ordering-eq.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(dex-boolean)  dex?

Returns a dex that compares boolean? values.

A call to this dex can be run through pure-run-getfx without problems.

Returns a cline that compares booleans by an ordering where #f is ordering-lt to #t.

A call to this cline can be run through pure-run-getfx without problems.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to (dex-boolean).

Returns a cline that compares booleans by an ordering where #t is ordering-lt to #f.

A call to this cline can be run through pure-run-getfx without problems.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to (dex-boolean).

procedure

(merge-boolean-by-and)  merge?

Returns a merge that merges booleans using and.

A call to this merge can be run through pure-run-getfx without problems.

procedure

(merge-boolean-by-or)  merge?

Returns a merge that merges booleans using or.

A call to this merge can be run through pure-run-getfx without problems.

procedure

(dex-immutable-string)  dex?

Returns a dex that compares immutable strings.

A call to this dex can be run through pure-run-getfx without problems.

Returns a cline that compares immutable strings by their string<? ordering.

A call to this cline can be run through pure-run-getfx without problems.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to (dex-immutable-string).

procedure

(dex-exact-rational)  dex?

Returns a dex that compares exact rational numbers.

A call to this dex can be run through pure-run-getfx without problems.

procedure

(cline-exact-rational)  cline?

Returns a cline that compares exact rational numbers by their < ordering.

A call to this cline can be run through pure-run-getfx without problems.

When the dex obtained from this cline using get-dex-from-cline is compared by (dex-dex), it is ordering-eq to (dex-exact-rational).

Returns a fuse that fuses exact rational numbers using +.

A call to this fuse can be run through pure-run-getfx without problems.

Returns a fuse that fuses exact rational numbers using *.

A call to this fuse can be run through pure-run-getfx without problems.

procedure

(assocs->table-if-mutually-unique assocs)  (maybe/c table?)

  assocs : (listof (cons/c dexed? any/c))
Given an association list, returns a just of a table with the same entries if the keys are mutually unique; otherwise returns (nothing).

This is a procedure that is convenient for two purposes: It’s useful for detecting duplicates in a list of dexed values, and it’s useful for constructing tables. These purposes often coincide, since data structures which contain mutually unique values are often good candidates for converting to tables.

procedure

(getfx-is-eq-by-dex dex a b)  (getfx/c boolean?)

  dex : dex?
  a : any/c
  b : any/c
Given a dex and two values, returns a getfx? computation that computes whether those values are ordering-eq according to the dex. The two values must be in the dex’s domain; otherwise, the computation raises an exn:fail:contract? exception.

procedure

(table-kv-map table kv-to-v)  maybe?

  table : table?
  kv-to-v : (-> dexed? any/c any/c)
Returns a table with the same keys as the given one. The result is constructed by iterating over the given hash table’s entries in an unspecified order and calling the given function with each entry’s key and value to determine the corresponding result entry’s mapped value.

procedure

(table-kv-all? table kv-accepted?)  boolean?

  table : table?
  kv-accepted? : (-> dexed? any/c boolean?)
Iterates over the given hash table’s entries in an unspecified order and calls the given function on each entry’s key and value. If the function ever returns #f, then the overall result is #f; otherwise, it’s #t.

There is no short-circuiting. Every entry is always visited, a policy which ensures that pure code can’t use nontermination or run time errors to make assertions about the iteration order of the table. (Nevertheless, impure code can use Racket side effects to observe the iteration order.)

procedure

(table-kv-any? table kv-accepted?)  boolean?

  table : table?
  kv-accepted? : (-> dexed? any/c boolean?)
Iterates over the given hash table’s entries in an unspecified order and calls the given function on each entry’s key and value. If the function ever returns #t, then the overall result is #t; otherwise, it’s #f.

There is no short-circuiting. Every entry is always visited, a policy which ensures that pure code can’t use nontermination or run time errors to make assertions about the iteration order of the table. (Nevertheless, impure code can use Racket side effects to observe the iteration order.)

procedure

(table-v-map table v-to-v)  maybe?

  table : table?
  v-to-v : (-> any/c any/c)
Returns a table with the same keys as the given one. The result is constructed by iterating over the given hash table’s entries in an unspecified order and calling the given function with each entry’s mapped value to determine the corresponding result entry’s mapped value.

procedure

(table-v-all? table v-accepted?)  boolean?

  table : table?
  v-accepted? : (-> any/c boolean?)
Iterates over the given hash table’s entries in an unspecified order and calls the given function on each entry’s mapped value. If the function ever returns #f, then the overall result is #f; otherwise, it’s #t.

There is no short-circuiting. Every entry is always visited, a policy which ensures that pure code can’t use nontermination or run time errors to make assertions about the iteration order of the table. (Nevertheless, impure code can use Racket side effects to observe the iteration order.)

procedure

(table-v-any? table v-accepted?)  boolean?

  table : table?
  v-accepted? : (-> any/c boolean?)
Iterates over the given hash table’s entries in an unspecified order and calls the given function on each entry’s mapped value. If the function ever returns #t, then the overall result is #t; otherwise, it’s #f.

There is no short-circuiting. Every entry is always visited, a policy which ensures that pure code can’t use nontermination or run time errors to make assertions about the iteration order of the table. (Nevertheless, impure code can use Racket side effects to observe the iteration order.)

3 Extensibility

 (require interconfection/extensibility/base)
  package: interconfection-lib

This module supplies an effect system designed for deterministic concurrency for the sake of implementing module systems. So that modules don’t have to be able to observe the relative order they’re processed in, this makes use of the orderless table? collections from interconfection/order/base. In turn, some parts of interconfection/order/base are designed to be able to read modular extensions of the currently running program using getfx? effects.

For now, nothing but some trivial getfx? effects are documented. The full system also has "extfx" effects which can read and write to definition spaces.

3.1 Read-only extensibility effects ("getfx")

procedure

(getfx? v)  boolean?

  v : any/c
Returns whether the given value is a representation of an effectful computation that performs read-only extensibility side effects as it computes a result.

procedure

(getfx/c result/c)  contract?

  result/c : contract?
Returns a contract that recognizes a representation of an effectful computation that performs read-only extensibility side effects and returns a value that abides by the given contract.

procedure

(pure-run-getfx effects)  any/c

  effects : getfx?
Attempts to run the given getfx? computation, raising an error if it attempts to read just about anything.

procedure

(getfx-done result)  getfx?

  result : any/c
Returns a getfx? computation that performs no side effects and has the given result.

procedure

(getfx-bind effects then)  getfx?

  effects : getfx?
  then : (-> any/c getfx?)
Returns a getfx? computation that proceeds by running the given effects getfx? computation, passing its result to then, and finally running the getfx? computation that results from that.

If both the subcomputations performed this way can be run through pure-run-getfx without problems, then so can the overall computation.

procedure

(fuse-getfx dexed-getfx-method)  fuse?

  dexed-getfx-method : (dexed-first-order/c (-> (getfx/c fuse?)))
Given dexed-getfx-method as a dexed function, returns a fuse that combines getfx? computations. The combined getfx? computation works by calling the dexed-getfx-method function and running its getfx? result to get a fuse; doing the same with both of the original getfx? computations to get each of their results; and fusing the results by that fuse. If the results turn out not to be in the fuse’s domain, this causes an error.

If the getfx? computation that results from dexed-getfx-method and the calls to its resulting fuses can be run through pure-run-getfx without problems, then so can a call to the fused getfx? computation.

A call to this fuse can be run through pure-run-getfx without problems.

When compared by (dex-dex), all fuse-getfx values are ordering-eq if their dexed-getfx-method values are.