### Binding [ Type environments Alias envs Datatype identifiers Alias ids Funcon identifier-tagged Alias id-tagged Funcon fresh-identifier Entity environment Alias env Funcon initialise-binding Funcon bind-value Alias bind Funcon unbind Funcon bound-directly Funcon bound-value Alias bound Funcon closed Funcon scope Funcon accumulate Funcon collateral Funcon bind-recursively Funcon recursive ] Meta-variables T <: values #### Environments Type environments ~> maps(identifiers, values?) Alias envs = environments /* An environment represents bindings of identifiers to values. Mapping an identifier to `( )` represents that its binding is hidden. Circularity in environments (due to recursive bindings) is represented using bindings to cut-points called `links`. Funcons are provided for making declarations recursive and for referring to bound values without explicit mention of links, so their existence can generally be ignored. */ Datatype identifiers ::= {_:strings} | identifier-tagged(_:identifiers, _:values) Alias ids = identifiers Alias id-tagged = identifier-tagged /* An identifier is either a string of characters, or an identifier tagged with some value (e.g., with the identifier of a namespace). */ Funcon fresh-identifier : =>identifiers /* `fresh-identifier` computes an identifier distinct from all previously computed identifiers. */ Rule fresh-identifier ~> identifier-tagged("generated", fresh-atom) #### Current bindings Entity environment(_:environments) |- _ ---> _ Alias env = environment /* The environment entity allows a computation to refer to the current bindings of identifiers to values. */ Funcon initialise-binding(X:=>T) : =>T ~> initialise-linking(initialise-generating(closed(X))) /* `initialise-binding(X)` ensures that `X` does not depend on non-local bindings. It also ensures that the linking entity (used to represent potentially cyclic bindings) and the generating entity (for creating fresh identifiers) are initialised. */ Funcon bind-value(I:identifiers, V:values) : =>environments ~> { I |-> V } Alias bind = bind-value /* `bind-value(I, X)` computes the environment that binds only `I` to the value computed by `X`. */ Funcon unbind(I:identifiers) : =>environments ~> { I |-> ( ) } /* `unbind(I)` computes the environment that hides the binding of `I`. */ Funcon bound-directly(_:identifiers) : =>values /* `bound-directly(I)` returns the value to which `I` is currently bound, if any, and otherwise fails. `bound-directly(I)` does *not* follow links. It is used only in connection with recursively-bound values when references are not encapsulated in abstractions. */ Rule lookup(Rho, I) ~> (V:values) -------------------------------------------------------- environment(Rho) |- bound-directly(I:identifiers) ---> V Rule lookup(Rho, I) ~> ( ) ----------------------------------------------------------- environment(Rho) |- bound-directly(I:identifiers) ---> fail Funcon bound-value(I:identifiers) : =>values ~> follow-if-link(bound-directly(I)) Alias bound = bound-value /* `bound-value(I)` inspects the value to which `I` is currently bound, if any, and otherwise fails. If the value is a link, `bound-value(I)` returns the value obtained by following the link, if any, and otherwise fails. If the inspected value is not a link, `bound-value(I)` returns it. `bound-value(I)` is used for references to non-recursive bindings and to recursively-bound values when references are encapsulated in abstractions. */ #### Scope Funcon closed(X:=>T) : =>T /* `closed(X)` ensures that `X` does not depend on non-local bindings. */ Rule environment(map( )) |- X ---> X' ------------------------------------------- environment(_) |- closed(X) ---> closed(X') Rule closed(V:T) ~> V Funcon scope(_:environments, _:=>T) : =>T /* `scope(D,X)` executes `D` with the current bindings, to compute an environment `Rho` representing local bindings. It then executes `X` to compute the result, with the current bindings extended by `Rho`, which may shadow or hide previous bindings. `closed(scope(Rho, X))` ensures that `X` can reference only the bindings provided by `Rho`. */ Rule environment(map-override(Rho1, Rho0)) |- X ---> X' --------------------------------------------------------------------- environment(Rho0) |- scope(Rho1:environments, X) ---> scope(Rho1, X') Rule scope(_:environments, V:T) ~> V Funcon accumulate(_:(=>environments)*) : =>environments /* `accumulate(D1, D2)` executes `D1` with the current bindings, to compute an environment `Rho1` representing some local bindings. It then executes `D2` to compute an environment `Rho2` representing further local bindings, with the current bindings extended by `Rho1`, which may shadow or hide previous current bindings. The result is `Rho1` extended by `Rho2`, which may shadow or hide the bindings of `Rho1`. `accumulate(_, _)` is associative, with `map( )` as unit, and extends to any number of arguments. */ Rule D1 ---> D1' ------------------------------------------- accumulate(D1, D2) ---> accumulate(D1', D2) Rule accumulate(Rho1:environments, D2) ~> scope(Rho1, map-override(D2, Rho1)) Rule accumulate( ) ~> map( ) Rule accumulate(D1) ~> D1 Rule accumulate(D1, D2, D+) ~> accumulate(D1, accumulate(D2, D+)) Funcon collateral(Rho*:environments*) : =>environments ~> checked map-unite(Rho*) /* `collateral(D1, ...)` pre-evaluates its arguments with the current bindings, and unites the resulting maps, which fails if the domains are not pairwise disjoint. `collateral(D1, D2)` is associative and commutative with `map( )` as unit, and extends to any number of arguments. */ #### Recurse Funcon bind-recursively(I:identifiers, E:=>values) : =>environments ~> recursive({I}, bind-value(I, E)) /* `bind-recursively(I, E)` binds `I` to a link that refers to the value of `E`, representing a recursive binding of `I` to the value of `E`. Since `bound-value(I)` follows links, it should not be executed during the evaluation of `E`. */ Funcon recursive(SI:sets(identifiers), D:=>environments) : =>environments ~> re-close(bind-to-forward-links(SI), D) /* `recursive(SI, D)` executes `D` with potential recursion on the bindings of the identifiers in the set `SI` (which need not be the same as the set of identifiers bound by `D`). */ Auxiliary Funcon re-close(M:maps(identifiers, links), D:=>environments) : =>environments ~> accumulate(scope(M, D), sequential(set-forward-links(M), map( ))) /* `re-close(M, D)` first executes `D` in the scope `M`, which maps identifiers to freshly allocated links. This computes an environment `Rho` where the bound values may contain links, or implicit references to links in abstraction values. It then sets the link for each identifier in the domain of `M` to refer to its bound value in `Rho`, and returns `Rho` as the result. */ Auxiliary Funcon bind-to-forward-links(SI:sets(identifiers)) : =>maps(identifiers, links) ~> map-unite(interleave-map(bind-value(given, fresh-link(values)), set-elements(SI))) /* `bind-to-forward-links(SI)` binds each identifier in the set `SI` to a freshly allocated link. */ Auxiliary Funcon set-forward-links(M:maps(identifiers, links)) : =>null-type ~> effect(interleave-map(set-link(map-lookup(M, given), bound-value(given)), set-elements(map-domain(M)))) /* For each identifier `I` in the domain of `M`, `set-forward-links(M)` sets the link to which `I` is mapped by `M` to the current bound value of `I`. */