Modules, Concatenation, and Better Solutions

John J Barton johnjbarton at
Tue Oct 16 20:38:59 PDT 2012

On Tue, Oct 16, 2012 at 7:10 PM, David Herman <dherman at> wrote:
> On Oct 16, 2012, at 4:51 PM, John J Barton <johnjbarton at> wrote:
> Concrete example: Even and Odd modules refer to each other, but the import statements occur after some initialization:
>     module Odd {
>         export let odd = function(x) {
>             return x === 0 ? false : !even(x - 1);
>         }
>         import even from Even; // force execution of Even here, if it hasn't already

Ah, I see the import has three possible duties here, 1) declaration of
|even| from Even 2) definition of |even| 3) execution of module code.
Its that last category that cause the issues in this example: the
circular value isn't used until the next stmt.

>         export let b = odd(17);
>     }
>     module Even {
>         export let even = function(x) {
>             return x === 0 || !odd(x - 1);
>         }
>         import odd from Odd; // force execution of Odd here, if it hasn't already
>         export let b = even(17);
>     }
>     console.log(Odd.b);
> With semantics (a), this executes like so:
> - start executing Odd
> - initialize Odd.odd
> - start executing Even
> - initialize Even.even
> - try to start executing Odd (but it's already started, so don't)
> - initialize Even.b by calling Even.even
> - initialize Odd.b by calling Odd.odd
> And everything succeeds. With semantics (b), regardless of how we break the tie in a cycle, this will fail, because it will try to call either Odd.odd or Even.even before it has initialized the function. Say it starts executing Odd first. It would do this:
> - start executing Odd
> - initialize Odd.odd
> - initialize Odd.b by calling Even.even
> - error: Even.even is not yet initialized
> So semantics (a) makes it more possible to make a cyclic dependency work, but it's still subtle if you're trying to let them refer to each other in top-level initialization code: you have to make sure to carefully place the imports late enough that the relevant pieces of each modules will have adequately initialized.

I'm surprised by (b). A behavior I would expect from a module loader (c)
 - start executing Odd
 - initialize Odd.odd
 - start executing Even
 - initialize Even.even
 - try to start executing Odd
 - error: Odd has not completed (circular).
Unlike (b) we load and execute Even until we discover the cycle.  (b)
seems to declare Even.even but rely on some other mechanism to define

I guess that all three allow Even/Odd cycles without the execution of b.

I guess (a) excludes later standardization of (b) or (c) but not the reverse.


More information about the es-discuss mailing list