### 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`.
*/