Skip to content

Commit 76641cd

Browse files
committed
Add answer to node_modules question
1 parent 8743821 commit 76641cd

1 file changed

Lines changed: 143 additions & 15 deletions

File tree

FAQ.md

Lines changed: 143 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* [Promise support?](#promise-support)
1616
* [ES2015 and beyond?](#es2015)
1717
* [Why a monorepo?](#monorepo)
18+
* [Why are library packages in a node_modules directory?](#lib-node-modules)
1819
* [How can I contribute?](#contributing)
1920

2021

@@ -155,9 +156,9 @@ To better support numeric computing, standards bodies can do the following:
155156
* __Universally unique identifiers__. Universally unique identifiers ([UUIDs][uuid]) are stored as 128-bit values.
156157
* __Arbitrary-precision arithmetic__. [Arbitrary-precision arithmetic][arbitrary-precision-arithmetic] is beneficial for high precision applications and in preventing overflow, computing fundamental mathematical constants, and evaluating precision errors in fixed-precision calculations.
157158

158-
1. __Large arrays__: add support for large arrays. Arrays are currently limited to [`2**32-1`][ecma262-array-length] (approximately `4` billion) elements. Many applications will never reach this limit; however, as datasets continue to increase in size, the need for larger arrays will become more apparent. For example, consider a `100000 x 100000` dense matrix, which is not uncommon when working with sensor data and trying to find correlations. This matrix will have `10` billion elements. Given current length limitations, one cannot store this data contiguously in a plain JavaScript array, thus resulting in increased cache misses and decreased performance.
159+
1. __Large arrays__: add support for large arrays. Arrays are currently limited to [`2**32-1`][ecma-262-array-length] (approximately `4` billion) elements. Many applications will never reach this limit; however, as datasets continue to increase in size, the need for larger arrays will become more apparent. For example, consider a `100000 x 100000` dense matrix, which is not uncommon when working with sensor data and trying to find correlations. This matrix will have `10` billion elements. Given current length limitations, one cannot store this data contiguously in a plain JavaScript array, thus resulting in increased cache misses and decreased performance.
159160

160-
__Aside:__ typed arrays and, more generally, array-like objects may have as many as [`2**53-1`][ecma262-tolength] elements. In the case of typed arrays, however, one must allocate memory upon instantiation; thus, growing a typed array as needed, while possible, is neither straightforward nor efficient.
161+
__Aside:__ typed arrays and, more generally, array-like objects may have as many as [`2**53-1`][ecma-262-tolength] elements. In the case of typed arrays, however, one must allocate memory upon instantiation; thus, growing a typed array as needed, while possible, is neither straightforward nor efficient.
161162

162163
1. __Typed objects__: add support for [typed objects][typed-objects-proposal]. Typed objects would facilitate efficient memory storage of data, which is critical for [performant][five-things-that-make-go-fast] numeric computations. In short,
163164

@@ -173,11 +174,11 @@ To better support numeric computing, standards bodies can do the following:
173174

174175
1. __Big numbers__: add support for big [integers][julia-bigint], [rationals][golang-big], and [floats][julia-bigfloat]. In addition to cryptography and computing irrational numbers, arbitrary precision arithmetic is useful for algorithms involving double-precision floating-point numbers. Currently, lack of efficient, and relatively performant, big number support limits the scope and types of implemented algorithms, including for basic transcendental functions.
175176

176-
1. __SIMD__: add support for long SIMD. Currently, [proposals][ecmascript-simd] for [SIMD][simd-js] in JavaScript have focused on [short SIMD][mozilla-simd], which is well-suited for graphics applications. However, [short SIMD][mozilla-simd] is __not__ particularly well-suited for large vector operations, which are common in numeric computing (e.g., BLAS).
177+
1. __SIMD__: add support for long SIMD. Currently, [proposals][ecmascript-simd] for [SIMD][mdn-simd-js] in JavaScript have focused on [short SIMD][mozilla-simd], which is well-suited for graphics applications. However, [short SIMD][mozilla-simd] is __not__ particularly well-suited for large vector operations, which are common in numeric computing (e.g., BLAS).
177178

178179
__Aside:__ JavaScript may never have native SIMD support. Instead, SIMD may be possible only via [WebAssembly][wasm]. Lack of native JavaScript SIMD support would be unfortunate, as plenty of applications exist (e.g., scripting for purposes of analysis and data manipulation), which would benefit from SIMD operations without requiring a context switch to a lower-level language and additional compilation steps.
179180

180-
1. __Parallelism__: add support for lightweight threading (parallelism). Currently, [data parallelism][data-parallelism], i.e., the same operations performed on different subsets of the same data, is only achievable by manual data orchestration and task execution via either [web workers][web-workers] (browser) or [child processes][child-process] (Node.js). While [web workers][web-workers] support [Transferable Objects][transferable-objects] thus allowing shared memory access, the same is not true for Node.js. Particularly in Node.js, task parallelism is heavyweight and cumbersome, especially for use cases like parallel computation involving matrix elements (e.g., compare to MATLAB's [`parfor`][matlab-parfor]). The ability to easily distribute data to a worker pool (processors) would provide a significant performance boost to many data analysis tasks.
181+
1. __Parallelism__: add support for lightweight threading (parallelism). Currently, [data parallelism][data-parallelism], i.e., the same operations performed on different subsets of the same data, is only achievable by manual data orchestration and task execution via either [web workers][mdn-web-workers] (browser) or [child processes][node-child-process] (Node.js). While [web workers][mdn-web-workers] support [Transferable Objects][mdn-transferable-objects] thus allowing shared memory access, the same is not true for Node.js. Particularly in Node.js, task parallelism is heavyweight and cumbersome, especially for use cases like parallel computation involving matrix elements (e.g., compare to MATLAB's [`parfor`][matlab-parfor]). The ability to easily distribute data to a worker pool (processors) would provide a significant performance boost to many data analysis tasks.
181182

182183
1. __GPGPU__: provide better support for [GPGPU][gpgpu]. Currently, performing general purpose GPU (GPGPU) computing tasks within a browser is only possible via [WebGL][webgl] and awkward usage of shaders, which are designed for generating graphics, not generic number crunching. Additionally, synchronous data transfers between the main thread and the GPU are expensive, debugging support is limited, and reading floating-point textures is not possible without workarounds which encode floating-point numbers into integer outputs (RGBA). While [compute shaders][compute-shaders] and [Vulkan][vulkan] promise better GPGPU support, we are years away from realizing their proposed benefits via JavaScript. Once realized, however, embarrassingly parallel computation tasks and machine learning techniques such as neural networks become more viable and efficient.
183184

@@ -279,6 +280,126 @@ The reasons are as follows:
279280
<!-- </faq-question> -->
280281

281282

283+
<!-- <faq-question> -->
284+
285+
---
286+
287+
<a name="lib-node-modules"></a>
288+
289+
### Why are library packages in a node_modules directory?
290+
291+
This project leverages the Node.js module resolution [algorithm][node-require] to resolve dependencies by name rather than by path. Hence, the project avoids relative require paths. For example,
292+
293+
``` javascript
294+
var foo = require( './../../../../../@stdlib/foo' );
295+
```
296+
297+
becomes
298+
299+
``` javascript
300+
var foo = require( '@stdlib/foo' );
301+
```
302+
303+
In general, far too many developers are oblivious to the module resolution [algorithm][node-require], often resorting to various unnecessary hacks, such as setting environment variables (e.g., `NODE_PATH`), using globals, creating symbolic links (symlink), using `require` wrappers, running startup scripts, or actually hacking `require`, itself (see [here][modifying-node-path-hack] and [here][list-of-require-hacks] as representative references). A superior approach is to leverage the module resolution [algorithm][node-require] to scope internal packages to their relevant context. For example, consider the following application directory structure
304+
305+
``` text
306+
/
307+
app/
308+
index.js
309+
a.js
310+
b.js
311+
node_modules/ # => local app dependencies
312+
debug/
313+
index.js
314+
logger/
315+
index.js
316+
routes/
317+
index.js
318+
login/
319+
index.js
320+
login.js
321+
post.js
322+
onerror.js
323+
logout/
324+
index.js
325+
logout.js
326+
post.js
327+
onerror.js
328+
node_modules/ # => local routes dependencies
329+
start/
330+
index.js
331+
end/
332+
index.js
333+
response-time/
334+
index.js
335+
models/
336+
index.js
337+
c.js
338+
d.js
339+
user/
340+
index.js
341+
e.js
342+
f.js
343+
node_modules/ # => local user model dependencies
344+
foo/
345+
index.js
346+
bar/
347+
index.js
348+
data/
349+
index.js
350+
g.js
351+
h.js
352+
i.js
353+
node_modules/ # => local data model dependencies
354+
transform/
355+
index.js
356+
analyze/
357+
index.js
358+
morph/
359+
index.js
360+
node_modules/ # => local models dependencies
361+
db-connect/
362+
index.js
363+
db-get/
364+
index.js
365+
db-set/
366+
index.js
367+
db-update/
368+
index.js
369+
db-delete/
370+
index.js
371+
node_modules/ # => 3rd party dependencies
372+
beep/
373+
boop/
374+
bop/
375+
```
376+
377+
where `g.js`
378+
379+
``` javascript
380+
var beep = require( 'beep' );
381+
var debug = require( 'debug' );
382+
var get = require( 'db-get' );
383+
var transform = require( 'transform' );
384+
var h = require( './h.js' );
385+
386+
// ...
387+
```
388+
389+
By leveraging node_modules, each local node_modules dependency
390+
391+
a. is scoped to its relevant context
392+
b. does not pollute the top-level node_modules directory which contains 3rd party dependencies
393+
c. allows modules within a scope to require the dependency by name rather than by relative path in a manner similar to node_module dependencies in parent scopes (including 3rd party dependencies)
394+
d. may be elevated to a higher scope without needing to update require paths
395+
396+
In short, the module resolution [algorithm][node-require] provides a simple and robust cross-platform solution for managing both external and local module dependencies.
397+
398+
__Aside__: A common objection to the directory structure above is that tools often ignore anything within a node_modules folder (e.g., linters, unit test runners, etc). That this project is able to configure tools to recognize files within node_modules folders is evidence to the contrary. If a tool cannot be configured otherwise, that is a flaw in the tool, not in the approach.
399+
400+
<!-- </faq-question> -->
401+
402+
282403
<!-- <faq-question> -->
283404

284405
---
@@ -299,7 +420,11 @@ See the [contributing guide][contributing-guide].
299420

300421
[dom]: https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model
301422
[canvas]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
423+
302424
[webgl]: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API
425+
[gpgpu]: https://en.wikipedia.org/wiki/General-purpose_computing_on_graphics_processing_units
426+
[compute-shaders]: https://www.khronos.org/opengl/wiki/Compute_Shader
427+
[vulkan]: https://www.khronos.org/vulkan/
303428

304429
[shiny]: http://shiny.rstudio.com/
305430
[bokeh]: http://bokeh.pydata.org/en/latest/
@@ -319,20 +444,32 @@ See the [contributing guide][contributing-guide].
319444
[npm]: https://www.npmjs.com/
320445

321446
[mdn-math]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
447+
[mdn-simd-js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SIMD
448+
[mdn-web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
449+
[mdn-transferable-objects]: https://developer.mozilla.org/en-US/docs/Web/API/Transferable
322450

323451
[native-math-bugs]: https://github.com/stdlib-js/stdlib/blob/develop/docs/native_math_bugs.md
324452
[contributing-guide]: https://github.com/stdlib-js/stdlib/blob/develop/CONTRIBUTING.md
325453

326454
[wasm]: https://github.com/WebAssembly/spec/
327455
[asm]: http://asmjs.org/spec/latest/
456+
328457
[numpy]: http://www.numpy.org/
329458
[scikit-learn]: http://scikit-learn.org/stable/
330459

331460
[semver]: http://semver.org/
461+
332462
[node-lts]: https://github.com/nodejs/LTS
333463
[node-addons]: https://nodejs.org/api/addons.html
464+
[node-require]: https://nodejs.org/api/modules.html
465+
[node-child-process]: https://nodejs.org/api/child_process.html
466+
467+
[modifying-node-path-hack]: https://lostechies.com/derickbailey/2014/02/20/how-i-work-around-the-require-problem-in-nodejs/
468+
[list-of-require-hacks]: https://gist.github.com/branneman/8048520
334469

335470
[ecma-262]: http://www.ecma-international.org/publications/standards/Ecma-262.htm
471+
[ecma-262-array-length]: http://www.ecma-international.org/ecma-262/6.0/#sec-arraycreate
472+
[ecma-262-tolength]: http://www.ecma-international.org/ecma-262/6.0/#sec-tolength
336473

337474
[golang-frexp]: https://github.com/golang/go/blob/c007ce824d9a4fccb148f9204e04c23ed2984b71/src/math/frexp.go#L27
338475
[golang-float64bits]: https://github.com/golang/go/blob/964639cc338db650ccadeafb7424bc8ebb2c0f6c/src/math/unsafe.go#L17
@@ -354,27 +491,18 @@ See the [contributing guide][contributing-guide].
354491
[symmetric-ciphers]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm
355492
[arbitrary-precision-arithmetic]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
356493

357-
[ecma262-array-length]: http://www.ecma-international.org/ecma-262/6.0/#sec-arraycreate
358-
[ecma262-tolength]: http://www.ecma-international.org/ecma-262/6.0/#sec-tolength
359-
360-
[simd-js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SIMD
361494
[mozilla-simd]: https://hacks.mozilla.org/2014/10/introducing-simd-js/
362495
[ecmascript-simd]: https://github.com/tc39/ecmascript_simd/
363496

364497
[five-things-that-make-go-fast]: http://dave.cheney.net/2014/06/07/five-things-that-make-go-fast
498+
365499
[typed-objects-proposal]: https://github.com/dslomov/typed-objects-es7
366500
[typed-objects-explainer]: https://github.com/nikomatsakis/typed-objects-explainer
367501

368502
[operator-overloading]: https://en.wikipedia.org/wiki/Operator_overloading
369503

370504
[data-parallelism]: https://en.wikipedia.org/wiki/Data_parallelism
371-
[web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
372-
[child-process]: https://nodejs.org/api/child_process.html
373-
[transferable-objects]: https://developer.mozilla.org/en-US/docs/Web/API/Transferable
374-
[matlab-parfor]: https://www.mathworks.com/help/distcomp/parfor.html
375505

376-
[gpgpu]: https://en.wikipedia.org/wiki/General-purpose_computing_on_graphics_processing_units
377-
[compute-shaders]: https://www.khronos.org/opengl/wiki/Compute_Shader
378-
[vulkan]: https://www.khronos.org/vulkan/
506+
[matlab-parfor]: https://www.mathworks.com/help/distcomp/parfor.html
379507

380508
<!-- </links> -->

0 commit comments

Comments
 (0)