Modules first or second class (Re: I noted some open issues on "Classes with Trait Composition")

# Claus Reinke (14 years ago)

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

Just a note on this: for me, that means Harmony modules are a step back from what I can implement in JS/now. Not having first-class modules has been a major drawback in Haskell (which has a strictly 1980s-style module system), leading to all kinds of work-arounds.

One of these workarounds, which I expect to see and use a lot in Harmony, is to have first-class modules-as-records (or objects) inside second-class built-in-modules.

Is this an intended outcome of the Harmony module design?

For instance, I've got a parser-combinator library and a (near) ES5 grammar built on it, and to test, I pass one module to the other, and both modules to the tester:

test(source, options, pc, grammar(pc))

Strictly speaking, the pc module also depends on the grammar module, for grammar-specified whitespace, but cyclic structures are a bit awkward in Javascript.

Claus

# Brendan Eich (14 years ago)

On May 20, 2011, at 2:51 PM, Claus Reinke wrote:

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

Just a note on this: for me, that means Harmony modules are a step back from what I can implement in JS/now.

That's false, since you cannot implement what second class modules provide in JS now without a preprocessor. You cannot, by special forms such as the module M = ... form, tell the compiler to prefetch or find memoized a certain module.

Not having first-class modules has been a major drawback in Haskell (which has a strictly 1980s-style module system), leading to all kinds of work-arounds.

Yes, the CMU folks were buggin'. But: please try to look at JS as it is, not just reference Haskell.

One of these workarounds, which I expect to see and use a lot in Harmony, is to have first-class modules-as-records (or objects) inside second-class built-in-modules.

Why?

If you want first-class objects, use objects (frozen if you like). If you want functions, they're in JS already.

Generative classes by nesting in a maker function fall out cleanly from this design.

Is this an intended outcome of the Harmony module design?

Please read the proposal page.

harmony:modules

It's all there: static scoping, orthogonality, etc.

# Kam Kasravi (14 years ago)

This is sort of an interesting point. In a related question I've wondered about how narcissus could use modules in lieu of

its current way of eval'ing definitions.const in jslex and jsparse in order to get the operators, tokens and keywords in local scope for both the lexer and the parser.If narcissus were to take advantage of harmony could jsparse, jslex do an 'import jsdefs.consts' as an alternative?

# David Herman (14 years ago)

Yep! By making modules static, we introduce much cleaner ways of sharing constants. Responding to Claus in a sec...

# David Herman (14 years ago)

Just a note on this: for me, that means Harmony modules are a step back from what I can implement in JS/now.

How is it a step back, if you can already implement it? We're not taking objects away from JavaScript.

Not having first-class modules has been a major drawback in Haskell (which has a strictly 1980s-style module system), leading to all kinds of work-arounds.

Usually when people deride the Haskell module system, they are comparing it to the ML module system, which... is also a second-class module system.

One of these workarounds, which I expect to see and use a lot in Harmony, is to have first-class modules-as-records (or objects) inside second-class built-in-modules.

Is this an intended outcome of the Harmony module design?

That's kind of a loaded question, given the word "workaround" but yes, it's definitely an intended outcome of the design to have second-class modules. Modules that are second-class have many benefits over first-class modules:

  • they are extremely lightweight, which is really important for ergonomics

  • they make it possible to do compile-time linking and variable resolution

  • they can share static information, such as sets of bindings (not that import * would not work with first-class modules), macros, or types (both of which are, IMO, worth considering in the future for ECMAScript)

Moreover, loaders make it possible to do dynamic linking. It's just a little harder than in e.g. SML. I say that's the right trade-off: writing simple modules is trivial, and writing modules with pluggable dependencies is still possible.

# David Herman (14 years ago)
  • they can share static information, such as sets of bindings (not that import * would not work with first-class modules)...

Oops, meant to say: "note that import * would not work"

# Claus Reinke (14 years ago)

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

Just a note on this: for me, that means Harmony modules are a step back from what I can implement in JS/now.

How is it a step back, if you can already implement it? We're not taking objects away from JavaScript.

If I implement a module loader[1] in JS/now, my modules are first-class functions from export objects to export object. Normally, that is just used to model import dependencies, but fully instantiated modules are (export) objects, and first-class modules fit right into the pattern.

You're advising that thinking about Harmony modules as first-class objects is not the best way to think about them.

Yes, I can continue to use first-class modules inside Harmony modules, but that makes two separate concepts where there used to be one.

Not having first-class modules has been a major drawback in Haskell (which has a strictly 1980s-style module system), leading to all kinds of work-arounds.

Usually when people deride the Haskell module system, they are comparing it to the ML module system, which... is also a second-class module system.

A hobby horse of mine [2, especially chapter 4 and sections 8.2/8.3]:-)

The proponents of Haskell's module system were explicitly looking for something simple (just namespacing), so it is not so much deriding as knowing that there are better solutions. I added a first-class module system to a dynamically typed functional language back in the 1990s, and from the literature then, it seemed that some ML implementations already had first-class modules (some had only higher-order functors, first-class in the separate module language).

Naturally, since I spent a lot of time thinking about the advantages of first-class modules, it is great to be able to implement them, including loaders, so easily in Javascript;-)

Modulo loaders, they are also easy in Haskell (I worked in Mark Jones' group for a while - he did some nice work in that area), but since most of the infrastructure is geared towards the second-class modules, first-class modules aren't used as much as their advantages would suggest.

One of these workarounds, which I expect to see and use a lot in Harmony, is to have first-class modules- as-records (or objects) inside second-class built-in- modules.

Is this an intended outcome of the Harmony module design?

That's kind of a loaded question, given the word "workaround"

No loading intended. I realize that the majority view might not yet call for first-class modules, might therefore find them counter-intuitive. Those developers might think of first-class modules as an abstract complication, rather than a simplifying and practical tool. It is just those who have grown used to their advantages who need workarounds in second-class modules.

but yes, it's definitely an intended outcome of the design to have second-class modules. Modules that are second-class have many benefits over first-class modules:

  • they are extremely lightweight, which is really important for ergonomics

I find modules-as-objects/records lightweight, but given the discussions I've seen on the CommonJS list regarding module formats, you're probably right for the majority of users.

  • they make it possible to do compile-time linking and variable resolution

For Javascript and other dynamic languages, compile-time for one piece of code is often runtime for its host code. Having a phase separation, after which all variables are resolved, is great but is that not addressed by destructuring?

  • they can share static information, such as sets of bindings (note that import * would not work with first-class modules), macros, or types (both of which are, IMO, worth considering in the future for ECMAScript)

Import * is useful, but isn't it based on the fixed shape of the module we import from? In a typed language, if there is a static type that includes the property names of an object, import * from that object becomes possible. And, in Javascript, wouldn't a restricted 'with', working only on frozen objects, serve the same purpose?

Btw, can I do 'import *' on a dynamically loaded module?

Types and modules are tricky, because of the various design options (some of which are mentioned in section 8.2 of [2]).

Moreover, loaders make it possible to do dynamic linking. It's just a little harder than in e.g. SML. I say that's the right trade-off: writing simple modules is trivial, and writing modules with pluggable dependencies is still possible.

And reflection represents module instances as objects. I was just surprised that you insisted on the second-class view as central (rather than it being a simplified view on first-class modules).

Thanks for the clarification, Claus

[1] libraryinstitute.wordpress.com/2010/12/01/loading-javascript-modules [2] Functions, Frames, and Interactions - completing a lambda-calculus-based purely functional language with respect to programming-in-the-large and interactions with runtime environments, Claus Reinke, 1998 community.haskell.org/~claus/publications/phd.html