Modules: compile time linking (was Re: Modules feedback, proposal)

Sam Tobin-Hochstadt samth at ccs.neu.edu
Sun Apr 1 13:58:25 PDT 2012


I think you misunderstand the relationship between what Dave said, and
the type Dynamic work.  Also, your later comments about staging are
unrelated to this issue, and wrong with regard to the module design.
More detail below ...

On Sun, Apr 1, 2012 at 10:52 AM, Claus Reinke <claus.reinke at talk21.com> wrote:
>> If you have dynamic modules, you can't use them to export any compile-time
>> constructs, like macros, static operator overloading, custom literals, or
>> static types. If you load a module at runtime, then it's too late by the
>> time you actually have the module to use it for anything at compile time.
>
>
> That is not quite accurate, unless I'm misinterpreting you. See, for
> instance, the variety of work surrounding type Dynamic in ML-like languages:
> it is possible to define language constructs that represent the
> static/dynamic phase distinction, so that
>
> - toDynamic(expression) pairs an expression with a runtime
>   representation of statically inferred information (such as type)
>
> - fromDynamic([info,expr]) extracts an expression from such a
>   pair *if and only if* the runtime info for expr matches the
>   statically inferred information for the usage context (since
>   this can fail, it is often embedded into pattern matching, aka
>   type case)
>
> toDynamic marks program points where objects pass out of
> range of static inference (eg, storing modules/objects to disk),
> fromDynamic marks program points where dynamically
> annotated objects need to be checked against static information
> before embedding them (such as linking a dynamically loaded module).

This is mostly a correct characterization of the academic work on type
Dynamic.  However, it's really missing the point that Dave was making.
 If you don't have static modules, nothing *static* can be exported
from them.  Using a facility like you describe, static information
such as types could be associated with the dynamic values exported by
a dynamic module after the fact.  For example, if a module exported a
two argument function, we could somewhere else use that operation as
the dynamic implementation of a static overloading of +.  However, the
static overloading itself can't be exported from the module.  Which is
exactly what Dave said.

> This pair of constructs allows for multiple program stages, each with their
> own static/dynamic phases. You can load a
> module at runtime, then enter compile time for that module,
> then enter a new runtime stage with the newly compiled and
> linked code. This was used, eg, in orthogonally persistent
> programming languages to dynamically store/load statically typed
> code/modules to/from database-like storage.

Again, this misunderstands the relationship between staging and
persistence.  Persistence is about *values* -- storing and retrieving
values of the language to a disk or database.  The type Dynamic work
shows how to do this safely in a typed language.  Multi-state
programming is about *programs*, not values.  Even when a value
contains computation, as with a function or an object, the *program*
is gone.

> Multi-stage languages drive this further by allowing stage-n
> dynamic constructs to set static properties for stage-(>n) code.
> Their motivation tends to be meta-programming.
>
> Which is why I'm confused by the staging aspects of ES6 modules:
>
> - ES is a multi-stage language, thanks to eval and module load
> - ES6 modules offer "static" constructs only for the first stage,
>   after which everything seems to devolve to dynamic again?
>
> I'm not saying this is wrong, since JS analysis is hard, but it
> would be good to be clear about whether this single-stage
> approach to a multi-stage problem is intended, or whether
> the current spec incompletely captures the intentions.

Neither of these is correct.

The modules design provides for both static and dynamic elements at
every phase.  In particular, if we have the program "A.js":

    import sin from "@math";
    console.log(sin(3));

and we load that from "B.js" with:

    System.loadAsync("A.js", m => m)

then the runtime of "B.js" is the compile-time of "A.js", and "A.js"
can use the static features of modules just fine.
-- 
sam th
samth at ccs.neu.edu


More information about the es-discuss mailing list