---------------------------------------------------------------------------------------------------
rpl 1.1 environment design
---------------------------------------------------------------------------------------------------
Monday, March 20, 2017

In rpl 1.1, we have modules with private namespaces.  Before rpl 1.1, an engine
kept (for incremental compilation) an environment that mapped identifiers to
patterns.

Earlier, in rpl 1.0, environments could be nested, i.e. it was possible to
create an environment that extended an existing an environment.  The new
environment appeared to "inherit" all the bindings of the existing environment,
and bindings created in the new environment could "shadow" bindings in the
existing environment.

Before Rosie v1.0, this capability was used in two ways:

  (a) to maintain a "base" environment containing primitive patterns
      ("primitive" meaning that they cannot be defined in rpl itself);
      when an engine was created, a new environment was created that was an
      extension of the "base" environment;

  (b) to create a private environment in which to evaluate "flavors" which are
      predefined macros (e.g. search/grep); the concept of "flavors" never made
      it into a release; it was an idea that allowed the "grep" capability to be
      implemented in a generic way; but it was implemented, and it did work;

In rpl 1.1, we have these requirements:

(1) Modules have private namespaces
(2) Modules can import other modules, accessing exported bindings using a
    package name as a prefix to form a qualified name, e.g. net.ipv4
(3) An importer cannot modify an imported module
(4) To accomodate future plans:
    (a) modules will contain macro (syntax transformation) definitions
    (b) modules will contain processing (function) definitions
    (c) modules MAY be parameterized one day (e.g. for the importer to supply
        patterns that will be used in a module)
    (d) modules MAY become first-class values

IMPLICATIONS for implementation:

Private namespaces
  Create a base environment in which the contents of module M are compiled
  After compilation, we have the "module environment" of M
  Make a list of bindings exported from M
  Make the exported bindings of M available to any module that imports M

Importing a module
  Suppose A imports M
  M must be available (i.e. already compiled) as a list of (exported) bindings
  The exported bindings of M are marked as AVAILABLE to A
  When evaluating rpl source in the environment of A, the prefix M causes
    identifier M.x to result in lookup(M, x)
    
Base environment
  Must contain primitives or the ability to import primitives
  To avoid the need to ALWAYS "import sys" or something like it, the base
    environment will contain primitives
  To enable the user to customize the base environment, at the risk of changing
    the meaning of (or even breaking) imported modules, we could provide a
    "standard prelude" that defines the base environment, and allow the user to
    supply their own prelude in its place.
  If we support a prelude concept, is there a way for the user to arrange things
    such that some imported modules see the standard base environment, and some
    see a custom one?  (And, is this a valid use case to support at this time?)

Importer cannot modify a module
  A module importing M cannot write to the "module environment" of M
  Can a module A which imports M change its local binding to, e.g. M.x?
  No rebinding will be allowed:
    * What is the use case for unbinding M.x in A? To ensure that the
      authors/maintainers of A do not accidentally use a pattern M.x which
      has been deemed unsuitable? 
      * Regardless of the motiviation, what would happen if module A could
        unbind M.x (within A)? It would appear to cause no harm.  The only
        effect is to render M.x unusable in A.  There is currently no syntax in
        rpl to unbind an identifier, and this use case seems too weak to create
        one.  A work-around for the use case, and perhaps a better style as
        well, is to create a module M' that imports M and exports all of M's
        bindings except for the offensive M.x.  
    * What is the use case for rebinding M.x in A?
        * The use case CANNOT be to affect the semantics of M.y, where M.y
          depends on M.x, because we want it to be the case that the meaning of
          M.y (as it appears in A) depends only on the module M in which it is
          defined.  We want this because it simplifies both automated analysis
          and human understanding of rpl code.
        * Semantically, the ability to rebind M.x in A is not very useful,
          because any new definition for M.x (in A) will have no relationship
          with the module M.  In other words, the rebinding of M.x in A can have
          no effects beyond A.  Thus, nothing is accomplished by rebinding M.x
          except perhaps to cause confusion in anyone reading the source of
          module A, who reasonably would expect the meaning of M.x to come from
          M, and not from A.
