Author: Michał Miszczyszyn (@mmiszy)
Introduce new syntax that allows for creating an object from imports:
import { a, b, fn } as Package from './package.js';
When using ECMAScript modules, it's often desired to import only certain named exports like so:
import { a, b, fn } from "./package.js";
This is currently part of the ECMAScript specification and is widely supported. However, some problems arise when using multiple imports in the same file.
It's often the case that small utility functions with vague names are being exported from modules, and they only make sense when put in a certain context. For instance:
import { fmap } from "./functor.js";
import { fmap } from "./applicative.js";
import { fmap } from "./monad.js";
Out of context, fmap
could be either of those implementations.
Importing two (or more) named exports from different packages results in a conflict of declarations:
import { a } from "./one.js";
import { a } from "./two.js"; // Error!
This can be currently solved by renaming the imports:
import { a as aOne } from "./one.js";
import { a as aTwo } from "./two.js";
Yet, it quickly becomes cumbersome when we have multiple imports such as when using utility libraries:
import {
map as lodashMap,
reduce as lodashReduce,
find as lodashFind,
filter as lodashFilter,
// … etc.
} from "./lodash.js";
import {
map as bluebirdMap,
reduce as bluebirdReduce,
find as bluebirdFind,
filter as bluebirdFilter,
// … etc.
} from "./bluebird.js";
Qualified Imports: a new syntax that allows importing multiple named exports and grouping them into a namespace. Compare:
import { fmap } as Functor from "./functor.js";
Functor.fmap(/* … */);
import { fmap } as Applicative from "./applicative.js";
Applicative.fmap(/* … */);
import { fmap } as Monad from "./monad.js";
Monad.fmap(/* … */);
import {
map,
reduce,
find,
filter,
// … etc.
} as Lodash from "./lodash.js";
import {
map,
reduce,
find,
filter,
// … etc.
} as Bluebird from "./bluebird.js";
Lodash.find(/* … */);
Bluebird.map(/* … */);
Even though this might work for certain scenarios, it makes it more difficult to reason about the modules being used. This is a problem not only for the developers due to the lack of readability but also for the tooling which might not be able to correctly determine whether particular exports are used or not.
Moreover, there's an overhead related to parsing and gathering all of the exports in a single namespace – compared to just a few we might want to use.
Haskell has rich syntax for imports and qualified imports:
import qualified Data.List (sort, fold, map)
Data.List.sort …
Data.List.fold …
Data.List.map …
Alternatively, imports can be grouped and renamed:
import qualified Data.Map.Lazy (lookup) as Map
Supposing that the module
Mod
exports four functions namedx
,y
,z
, and(+++)
:
Import command | What is brought into scope | Notes |
---|---|---|
import Mod |
x, y, z, (+++), Mod.x, Mod.y, Mod.z, (Mod.+++) |
(By default, qualified and unqualified names.) |
import Mod () |
(Nothing!) | (Useful for only importing instances of typeclasses and nothing else) |
import Mod (x,y, (+++)) |
x, y, (+++), Mod.x, Mod.y, (Mod.+++) |
(Only x , y , and (+++) , no z .) |
import qualified Mod |
Mod.x, Mod.y, Mod.z, (Mod.+++) |
(Only qualified versions; no unqualified versions.) |
import qualified Mod (x,y) |
Mod.x, Mod.y |
(Only x and y , only qualified.) |
import Mod hiding (x,y,(+++)) |
z, Mod.z |
(x and y are hidden.) |
import qualified Mod hiding (x,y) |
Mod.z, (Mod.+++) |
(x and y are hidden.) |
import Mod as Foo |
x, y, z, (+++), Foo.x, Foo.y, Foo.z, (Foo.+++) |
(Unqualified names as before. Qualified names use Foo instead of Mod .) |
import Mod as Foo (x,y) |
x, y, Foo.x, Foo.y |
(Only import x and y .) |
import qualified Mod as Foo |
Foo.x, Foo.y, Foo.z, (Foo.+++) |
(Only qualified names, using new qualifier.) |
import qualified Mod as Foo (x,y) |
Foo.x, Foo.y |
(Only qualified versions of x and y , using new qualifier) |
source: https://wiki.haskell.org/Import
Modules in Purescript can be qualified and they allow for selective imports:
import Data.List (sort, fold, map) as List
C# supports namespaces and exports of namespaces. Such namespaces can be imported later on like so:
using Sorter = Data.List.Sorter;
OCaml et al. support local opens, which are somewhat similar to qualified imports because they allow for importing a module and using it locally without polluting the global scope. For instance:
(** OCaml *)
let _ =
let open Log in
make ()
|> (logStr ("Hello"))
|> (logStr ("everyone"))
|> print
// ReasonML / ReScript
let _ = Log.(
make()
|> logStr("Hello")
|> logStr("everyone")
|> print
);