6
$\begingroup$

In JavaScript, accessing items from a module is done using ., e.g. moduleBar.moduleFoo.functionFoo, which looks exactly the same as accessing a property of an object, e.g. objectFoo.propertyFoo.

In Rust, you use :: to navigate through modules, such as module_bar::module_foo::function_foo, but use . for accessing a property of a structure, e.g. structure_foo.property_foo.

What's the benefit of Rust's approach? The obvious thing is that it's easier to tell at a glance what a is when you write a::b or a.b. If this is the only reason (or one of the bigger reasons), then why does this matter in practice so much that it justifies introducing a token to the language, since "structure vs. module" is really the only difference you can infer purely based on the token used to "drill through" an entity (e.g. you can't even tell if b is a module again, or a function, or a property (let alone its type) -- which is arguably a piece of information much more commonly required to be inferred as quickly as possible when reading code).

$\endgroup$
3
  • 1
    $\begingroup$ For more languages on both sides: C# uses . for namespaces, while C++ (like Rust) uses ::. $\endgroup$
    – Bbrk24
    Nov 26, 2023 at 18:33
  • $\begingroup$ In ECMAScript, the answer to almost every question is: because history. Modules existed as a design pattern in ECMAScript long before they became a language feature. They were implemented as objects with properties, which of course are dereferenced with .. So, when modules became a language feature, they retained this for backwards compatibility. In fact, modules still behave very much like objects. $\endgroup$ Nov 26, 2023 at 19:06
  • $\begingroup$ In JS you never navigate through modules. The closest you can get to moduleBar.moduleFoo.functionFoo is if moduleBar reexports moduleFoo, however, that is the same as exposing it as a property. There is no meaningful difference, and ends up resolved as property navigation anyway. Since a module reexporting another is not special. It just has a property that points to its export. export moduleBar from "./moduleBar" can be swapped for export cost moduleBar = /* ... */ and the result is the same on the consuming side. $\endgroup$
    – VLAZ
    Nov 27, 2023 at 7:50

1 Answer 1

3
$\begingroup$

The reasons for different lexical and structural choices are going to vary from language to language, as are uses terms like "module", "package", "namespace", and so on, but a key distinction between module access and member access is often that module access is static: a::b definitively statically resolves to an element of that module. It's not so much about knowing that the thing on the left is a module as knowing that the lookup is manifest and that this specific entity is referenced. This can ease static analysis tooling and make local analysis by the programmer simpler, but it's not necessarily an essential distinction.

In Java, which uses . for all navigation, java.lang.String.format("") could be calling format on the String class inside the java.lang package — or it could be a call chain starting with the java field of a superclass, and there's no local way to tell which is which. You have to do inheritance resolution to know which is happening. On the other hand, the compiler will be doing that resolution anyway, and the :: token was left free for method references later.

For your example of JavaScript, the distinction is a little different: JavaScript modules don't actually themselves have any meaningful existence, but an import statement can bind some exported declarations into an object. That object is first-class and you can do anything you could with any other object to it, including pass it as an argument, alias it, introspect it, and dereference members of it. This is a genuine semantic difference from a static module system, including Java's; the ability to export default a function, which then needs to act like a function, is also a novelty. The module system was also retrofitted into the language, to replace various proto-module systems that of necessity used objects, so easing the transition is a factor — although I'm not sure they'd do it any differently regardless.

Not every language draws this distinction, or has the sort of module system where the question comes up. It's clearly not essential, but languages do make lots of design choices to match their priorities that aren't essential.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .