papers/opinions/JavaScriptPackages

Version 1 (modified by scott, 7 years ago)

How JavaScript's package system should really work

Giving JavaScript a package system

In  Brendan Eich's recent post regarding  JavaScript 2, he included  slides that mentioned the introduction of a first class package system to JavaScript which appears to mimic the semantics and syntax of Java. First let me say I'm not fundamentally opposed to this in a language, but I believe that JavaScript is not suited for this type of package system. Second, the Java package system itself is a little bit lacking. I work with Java on a daily basis and at  the company I work for we have frequent technology discussions. The broken behavior and semantics of Java's package system have been a common topic of during these talks. I will reserve details about these behaviors for a future writing.

In those slides, Brendan quoted me saying "JavaScript needs a better extension mechanism". And while I believe this to be very true, and I was very honored to have been quoted in his talks, I had high hopes for a bit more creativity than simply borrowing Java's package system. So rather than simply talking about what is wrong with what is there, this time I would like to introduce what I believe to be the more appropriate package system.

First let's look at Java's package system. In Java, the directory a class lives in within the class path is used to load compiled classes. It's required that the package declaration at the begining of a Java source will match the compiled classes location in the class path. This makes the package declaration a duplication that little more than affirms the assumed.

All class files within that directory must have the same package declaration, thus living in the same package. The non-portable file system separator (slash, blackslash, colon whatever it may be on any given platform) is replaced with dot notation providing a syntax similar object dereferencings. If we are looking for a more web-oriented language definition, we do not need to provide this supplemental syntax because directory separators are uniform, as dictated by RFC 1783 (Uniform Resource Locators) and RFC 3986 (Uniform Resource Identifiers). Java was not necessarily assumed to be web-oriented, and thankfully so because that is why it's still largely used in back ends (while it's not so largely used for applet web front end interfaces as it once was).

With this in mind, for a web oriented programming language we can include uniform resource identifiers as a part of the language, eliminating the previous concerns with platform portability.

Explicit Dereferencing: URLs

Let's now assume, for a moment, that we are to introduce classes to JavaScript. And let's compare this idea to the current Java package mechanism for explicit dereferencing.

Figure 1.1 - Java explicit dereferencing

org.blisted.example.Class.method() /* Call a static method */

Interestingly enough, and despite the dozen or more import statements at the top of every Java source, Java has a very clever dependency handling mechanism. During compilation, it is decided that org.blisted.example.Class lives in org/blisted/example/Class.class. This dependency is looked up and it's utilization verified during compilation. Then at runtime, the byte code in org/blisted/example/Class.class is loaded. Even without an import statement.

In order to give JavaScript an appropriate sophisticated dependency handling system, something similar should happen. However, there is really no reason to make JavaScript anything other than runtime compiled, so the compilation pass can very easily be when dependant code is acquired, and then compiled, in the order it is found.

Figure 1.2 - JavaScript explicit dereferencing

http://blisted.org/example/Class.method()

To introduce such a mechanism, certain assumptions must be made. The first being that Class lives in a file called "Class" with a certain extension, we'll assume from here on, that its the "js" extension. So this means we are assuming that http://www.blisted.org/example/Class.js contains a class named "Class". Otherwise, we would have to provide an mechanism that lets us look into a file, and then a class within that file, and then a method within that class. The syntax would be unnecessarily complicated, and providing this assumption based syntax is actually beneficial because it enforces uniformity, which has been an important factor to the successes of both Java and Python. Given that programmers will do things strangely when they are able, even a "web scripting" language can benefit from such enforced uniformity.

Importing

Using explicit package name spaces for every call and declaration gets exhausting quickly. Import declarations allow application developers to alias an identifier (an interface, class, method or value) locally, so they may use it's package local name anywhere after the import statement is found. This is one part of the Java package system that is believed to be very useful, and is not worth omitting.

Figure 2.1 - Java Import

import org.blisted.example.MyClass

class Example {
    public static void main (String[] arguments) {
        MyClass foo = new MyClass();
    }
}

With our URL based dependencies, we would not want or need to change the semantics of these types of imports.

Figure 2.2 - Theoretical URL Import

import http://blisted.org/example/MyClass

var foo = new MyClass()

/* imagine a few dozen lines of code here */

var bar = new MyClass()

Obviously, if an item is needed more than once, it's a lot nicer to import it than it is to explicitly dereference it.

Figure 2.3 - JavaScript URL dereferencing

var foo = new http://blisted.org/example/MyClass()

/* imagine a few dozen lines of code here */

var bar = new http://blisted.org/example/MyClass()

Sarbanes Oxley: URIs

Now if you work in an enterprise environment for a publicly traded company in the united states, or just a careful company anywhere else, you may be already saying to yourself that won't work because it depends on production. But in reality, no, that's not true. It doesn't rely on production any more than any other web based technologies such as CSS or any other inclusion mechanism does. Obviously, introducing URLs to a language to facility a package system also should mean that relative URIs are available, as per Section 4.2 in RFC 3986. This relative URI mechanism could potentially allow developers to utilize package name spacing within relative locations to provide clean testable applications.

Figure 3.1 - Relative URI imports

/* import the "Test" class from "js/file/Test.js" 
 * off the top of this domain 
 */
import /js/file/Test

/* import the "Foo" class from "example/Foo.js" relative 
 * to whatever the location of this current source code is
 */
import example/Foo

Decisions

  1. I don't think certain fans of the Java programming language will be terribly fond of the syntax that introduces slashes as a name space separator in any programming language. But there is a very simple reason for this, in Java we have the ability to decide that java.util.Map.Entry belongs to the class java.util.Map, rather than the class Entry within the package java.util.Map, by navigating the local class path. This is not possible with more internet based semantics, because the entire world would have to be loaded first. For this reason, we provide a very clear distinction between name space separation and package separation.

With this in mind,

import http://mozilla.org/util/Map.Entry

would be roughly equivalent to

import java.util.Map.Entry

and quite obviously more explicit.

  1. For better or worse, an increasing amount of literal syntax is being introduce to JavaScript. In Brendan's talks at  The Ajax Experience and  XTech 2006, the introduction of Date literals were mentioned the introduction of date literals based on ISO 8601. The ECMA-357 E4X specification introduces XML literals to ECMA Script, which is largely implemented in the rhino (Java) and spidermonkey (C) mozilla JavaScript implementations. Adding URL literals at this point is a pretty minor deal.

Why this works

  • URIs can be relative, this may mean relative to the operating URL, or relative to the local file system location. This system works for both web and non-web based applications.
  • Introduces runtime loading while providing name spaces that can be used for protecting implementation details.
  • Namespaces depend on the current location of the code, which can be moved, packages that are utilized locally will have different name spaces than the examples in documentation. However, code released by generous authors can be hosted at remote locations. This already exists with the current semantics of JavaScript, but we're now introducing the ability to provide some protection for certain types of application code.
  • Namespaces for local code can easily be compromised. Packages can be compromised in Java similarly by using the same package name space at a different point in the class path. So we aren't solving, but we also aren't introducing, this problem.
  • It's easy to understand.

Error: Failed to load processor AddComment
No macro or processor named 'AddComment' found