proposal-built-in-modules icon indicating copy to clipboard operation
proposal-built-in-modules copied to clipboard

Split or unified namespaces?

Open littledan opened this issue 6 years ago • 28 comments

Should built-in modules be all with one scope/scheme prefix in their module specifier, or should we have multiple prefixes based on what kind of module they are? I've heard different opinions from different people, so I though it could be good to collect the arguments all in one thread.

Shared namespace

We could put all built-in modules with a specifier like "@std/module" or "std:module", regardless of where they come from or what they're for.

Advantages:

  • It's often not clear how to categorize a module, as modules may be gradually introduced into different embedding environments over time, with an attempt to match each other.
  • Coordination in use of this namespace can take place either implicitly--if nothing else, browsers will eventually implement things and notice overlaps--or explicitly, e.g., through something like https://github.com/littledan/js-shared-interfaces/blob/master/MODULES.md .
  • Developers don't really think about standards bodies when programming, and standards bodies don't really cleanly map onto embedding environments or logical spaces in practice, so it would be confusing to subject them to all that.

Split namespace

Several different namespaces for built-in modules could be used, such as "@js/", "@html/", "@nodejs/" (or, "js:", "html:", "nodejs:"), for different modules.

Advantages:

  • Different standards bodies or embedding environments could use different prefixes, reducing the need for coordination between them, so they can each create new modules independently.
  • The prefix could be used indicate which subset of environments a module is valid in, making it more apparent how to write code that works across multiple environments.
  • When multiple environments have minor differences between the semantics of a similar module, they can use the same name with a different prefix, to indicate the difference.

Next steps

What are your thoughts?

littledan avatar Dec 17 '18 15:12 littledan

My current feeling is that the web + JS should use a single namespace, but other environments like Node.js might use additional namespaces for Node-specific functionality.

littledan avatar Dec 17 '18 15:12 littledan

I think that the Web should just be an environment where JS is used, and it should not be treated special in regard to namespaces. I think everything that is web-specific (web standards) should be namespaced.

mcollina avatar Dec 17 '18 15:12 mcollina

I'm trying to wrap my mind around this.

I think it makes sense for Node.js but I'm not sure how it would work for other environments. Here's my best guess:

import { Promise, Worker, Atomics, ArrayBuffer } from '@std/common';
import { document, window, DocumentFragment } from '@std/dom';
import { writeFile, readFile } from '@nodejs/fs';
import cluster from '@nodejs/cluster';
import { app, BrowserWindow } from '@electron/common';
import desktopCapturer from '@electron/desktopCapturer';

Is Electron an environment or should it stick to user-land modules?

styfle avatar Dec 17 '18 22:12 styfle

I don't think there's any particularly reasonable slicing of things for the web platform to use separate namespaces, so it should all be on one namespace. (Plus, in general, separate namespaces means I'll have to remember two names for each API - the actual name, and the namespace that is arbitrarily associated with it.

tabatkins avatar Dec 17 '18 22:12 tabatkins

and the namespace that is arbitrarily associated with it

That's not a legitimate worry unless you have an example of a module that would be warranted in two different scopes. If you know what you are importing the scope is obvious.

Mouvedia avatar Dec 17 '18 23:12 Mouvedia

URL is warranted in both browsers and node, but not in a context without an internet connection (like an embedded JS engine in a device).

ljharb avatar Dec 17 '18 23:12 ljharb

I think that's off topic but in this case Id just mirror it on the other scope.

Mouvedia avatar Dec 17 '18 23:12 Mouvedia

@ljharb In the case of URL how do you suggest this scenario is handled? Does the embedded JS engine avoid exporting URL or does URL need to be in a different namespace such as @std/net because it's not "common" enough?

import { Promise, Worker, Atomics, ArrayBuffer } from '@std/common';
import { document, window, DocumentFragment } from '@std/dom';
import { URL, fetch } from '@std/net';

styfle avatar Jan 03 '19 22:01 styfle

@styfle I think that's why having things be split isn't a good idea - if there's only a single namespace, then the user doesn't have to guess about what category the thing they want is in - they just import it, and if it's not there, it'll error out prior to evaluation.

ljharb avatar Jan 03 '19 22:01 ljharb

I think having multiple namespaces is fundamental for the ecosystem is governance and standardization. Who owns/organizes the non-prefixed namespace? Who owns/organizes the std namespace? I think this is far more important than the actual syntax.

If it becomes a recommended pattern that a runtime/platform can implement/add its own runtime specific part of the std lib, then it negates the benefits of having a std library in the first place. If the goal for the standard library is to include browser specific APIs, then it would be a huge loss for the ecosystem for non-browser JS implementations. I think the only solution is to have multiple namespaces, so it’s clear for the user what that API is about. It also makes polyfills easier, because it’d be clear where there are tradeoffs.

Let me make an example: having URL in the standard library would be a great win as it’s a simple data structure. Having fetch would become problematic for non-browser environments, mainly because fetch has some defined semantics regarding caching, security and connection management that make sense only if the JS vm is associated with a single user.

I don’t think having multiple namespaces would be a huge problem for the community: people need to read the docs anyway and most editors offers great autocomplete.

mcollina avatar Jan 03 '19 23:01 mcollina

I lean towards a shared namespace but to make an educated guess Id need at least the first 10 or so modules that will be available once std is implemented. If you are planning on organic additions, that might clash with the chosen approach if the eventual modules are not the ones expected.

In that regard, Id recommend to go with the unified approach as the desired outcome and if it fails, allow/enable the split if it's really warranted.

TL;DR: your first goal should be to make std worth using and design it as a template to enable the possibility of other environment-specific scopes.

Mouvedia avatar Jan 04 '19 05:01 Mouvedia

I think the issue of how to manage the shared namespace could become more significant if we increase the velocity of what we standardize, which I hope this effort will enable. However, I agree with many points made here about the benefits of a shared namespace.

We've also been using names which are less likely to overlap--in web standards, it's often a single additional global whose name starts with something like Web, and in TC39, we often avoid creating additional globals. Some new web standards (especially from WHATWG) play more in the space of ergonomic names, which is great, and I hope standard modules give the freedom to continue in this direction.

So far, the governance of the shared global object namespace has been implicitly based on browsers: any overlaps get noticed in their development process by the time they're looking into implementing and shipping something, if not before. So far, this has worked just fine, but it's worked in an environment with less potential contention by design than we might have in the future.

Do we want to continue this browser-based namespace governance, or should we do something more explicit to coordinate between standards bodies to identify potential conflicts earlier in the design process? I'm optimistic that we can cooperate here. To the people who want split namespaces: What challenges do you see in cooperating here?

littledan avatar Jan 04 '19 09:01 littledan

So far, the governance of the shared global object namespace has been implicitly based on browsers: any overlaps get noticed in their development process by the time they're looking into implementing and shipping something, if not before. So far, this has worked just fine, but it's worked in an environment with less potential contention by design than we might have in the future.

The current global namespace recommends every runtime to add its own things to it, meaning that there is no single source of truth to what you can expect being global in a JS environment. The benefit of a standard library is that it is ubiquitous. If every different JS environment can add to the standard library things that could not be implemented by a generic JS runtime (they are specific to a browser or a server for example), i.e. if pieces of the standard library become optional, then the benefit of having a standard library collapses for an ergonomic point of view when creating isomorphic code.

Multiple namespaces enable the web to standardize all the APIs that make sense in a Browser context and at the same time it enables non-Browser environments to not do so. As an example, the DOM could be standard, and there will no question for an IoT runtime to have the DOM or not. Having multiple namespaces (or sub-namespaces) acknowledges the fact that JS can run in non-browser environments. Developers will know from looking at the import if a module is expected to be there or not. In turn, this create less pressure for non-browser runtimes to not behave like a browser as those modules will be "web modules".

I think the standard namespace should be limited to utilities of the core part of the language and do not assume where something could be run. Would you expect the engines to include full HTTP/HTTP2/HTTP3 capability, a database, a caching layer, a file system API? I think the greatest thing about JS that enabled so much creativity and innovation was that none of those things where specified.

mcollina avatar Jan 04 '19 09:01 mcollina

I don't see what that has to do with shared/split namespaces. What if we list the pieces of the standard library that are required in the JS spec, and make sure to document what interfaces are supported in what environments carefully, e.g., in MDN?

littledan avatar Jan 04 '19 09:01 littledan

What if we list the pieces of the standard library that are required in the JS spec, and make sure to document what interfaces are supported in what environments carefully, e.g., in MDN?

That could potentially work, but it would create a lot of confusion on why a certain module is not in a given runtime as it will be buried at the end of a MDN page. There are JS standards and Web standards, this difference is important to developers and they should be immediately aware of it. Moreover creating a namespace mechanism enables runtimes to add custom or experimental behavior in a safe way without any potential collisions.

I think adding a standard library is the perfect moment to reduce the friction when writing isomorphic applications. If a developer needs to look at the bottom of an MDN page to know where that part of the standard library work, then this mechanism is only slightly better than adding globals.

mcollina avatar Jan 04 '19 10:01 mcollina

Looking at https://github.com/denoland/deno_std I think @ry might have an opinion on the matter.

Mouvedia avatar Jan 04 '19 10:01 Mouvedia

I'm totally in support of reducing the friction when writing isomorphic applications. I don't think sprinkling prefixes throughout JS source implying, "things defined outside of TC39 are not available for isomorphic code" is the best way to do that, since it's already not true.

littledan avatar Jan 04 '19 11:01 littledan

I think @mcollina is making some good points, and perhaps I can add to it with a concrete example.

Certainly we want to import temporal (a language-level/isomorphic builtin) like so:

import { ZonedInstant } from 'std:temporal';

Good so far.

From a strictly web/browser point of view, it does make sense to share the namespace:

import { asyncLocalStorage } from 'std:async-local-storage';

OK, that seems fine.

But if we extend that methodology to Node:

import { readFile } from 'std:fs';

That just doesn't look right at all! It feels much more appropriate to have something similar to:

import { readFile } from 'node:fs';

Taken together, the situation appears to be that we have priveledged the web and browsers over Node.

Is that a good thing?

zenparsing avatar Jan 04 '19 19:01 zenparsing

Why does it make sense to you to put localStorage in the standard namespace, but not fs? To me that seems like implicit browser bias - I think it either makes sense for neither to do so, or both - iow, both being in the standard namespace "look right" to me.

ljharb avatar Jan 04 '19 20:01 ljharb

Taken together, the situation appears to be that we have priveledged the web and browsers over Node.

Is that a good thing?

As both JS and the Web Platform are based on specced standards, and Node not necessarily - I guess that is fine

kenchris avatar Jan 08 '19 19:01 kenchris

Thanks for posting this. I had not been paying attention to this thread, so I don't know the particular issue yet (will read). But on the general issue stated here, I think it would be terrible for us to privilege the browser/web over Node, or over any of the increasing number of other host environments (IoT, blockchain, ...)

erights avatar Jan 09 '19 03:01 erights

Just dipping in, but the problem with 'std:async-local-storage' is: which standard? Say Node were standardized. 'std:fs' would still be wrong. You folks are all too acclimated to the web. To those of us focused on other uses of js, 'std:async-local-storage' is just as wrong.

erights avatar Jan 09 '19 04:01 erights

@erights I think the idea with a standard library is low level apis that for sure exist in all environments. Similar to what already exists, but not all bundled in the global context

obedm503 avatar Jan 09 '19 05:01 obedm503

You folks are all too acclimated to the web.

i don't see anything wrong with prioritizing web use-case, as it reflects industry reality. pretty much all javascript software-development in industry deals with (or is ultimately meant to support) passing workflow-data to/from web.

kaizhu256 avatar Jan 09 '19 06:01 kaizhu256

That’s simply not the case any more, even if it may have been at one time.

ljharb avatar Jan 09 '19 16:01 ljharb

pretty much all javascript software-development in industry deals with (or is ultimately meant to support) passing workflow-data to/from web.

That is nowhere even close to true.

erights avatar Jan 09 '19 18:01 erights

A thing that would help here is taking ArrayBuffer, pretending it were invented in a namespaced world, and explaining the effects of that in a split namespace system. What would be the expected outcome for such a feature and how costly is that for all parties involved?

annevk avatar May 20 '19 12:05 annevk

I would prefer splited namespace because I expect proprietary JavaScript embedding to expose some of their functionalities using modules within a non standard prefix.

If we do share the same prefix we could end up with collisions and incompatible APIs.

xtuc avatar Jun 05 '19 13:06 xtuc