Comment by drauh
11 years ago
The equivalent in C would then be replacing
#include <math.h> and "-lm"
with
#include <sin.h> #include <cos.h> #include <tan.h> #include <sinh.h> …
and "-lsin -lcos -ltan -lsinh …"
Which is nuts no matter what language you are coding in.
The difference with C is that with a good linker, functions you don't call are removed from the resulting binary (or are maybe not even in the binary if you are dynamically linking to the c runtime).
With javascript however, if you import a hypothetical "math.js" instead of just "sin.js", "cos.js" or "tan.js", then you'll need to download and evaluate a whole bunch of javascript that you might not need.
I'm not defending javascript, because I dislike it and avoid it where possible, but I can understand some of the reasons why short, specific modules came about.
Webpack 2 (probably the closest thing to a JS linker) can do that same tree shaking.
what you describe is the "-flto" option in GCC and I believe the equivalent in JS is donr by Google Closure compiler.
He described not Link Time Optimization (-flto), but the very basic linker functionality: to only include required functions. C/C++ has a weird compilation model where each source file is translated to machine code separately, with placeholders for unknown function addresses. Thus it is trivial to take only required functions.
-flto, on the other hand, allows to reverse this process to allow interprocedural optimization across different translation units.
4 replies →
> #include <sin.h> #include <cos.h> #include <tan.h> #include <sinh.h>
I've written worse - at least those cover multiple variations each (or overloads in C++ for float/double/std::complex?/...)
While I'm not a fan of the enforced java route of 1 file = 1 class, I do trend towards 1 file ~ 1 (main, public) thing - which might be a single function with no overloads. #include <string_left_pad.h>? Better than #include <everything.h>, which I see far too often...
I don't have to figure out which grouping of things my coworkers decided to throw something into if I can just #include by name - whereas aggregation headers more often than not will trigger a full project file-system search.
Unnecessary #include s don't accumulate nearly so much when you don't have to audit 100 functions to see if any of them use features from said header.
I don't trigger a rebuild of everything when the only things that #include stuff actually need it.
Lots of benefits!
> and "-lsin -lcos -ltan -lsinh …"
Or sin.o, cos.o, tan.o, sinh.o... which suddenly sounds a lot more reasonable. I wouldn't bat an eye at string_left_pad.o either.
Sure, I want to use tools to simplify the use of those micro-modules by aggregating them into libraries for convenience. But that's not a knock against micro-modules - just their packaging and tooling. Rewind time back far enough and I would've been manually specifying .o files on the command line and kvetching too...
Java doesn't enforce 1 file = 1 class but rather 1 file = 1 public class, which is exactly what you asked for. You can put as many private classes in the file as you want.
This isn't accurate either. Java does not enforce 1 file = 1 public class but rather 1 file = 1 public top-level class.
For example this is totally legit:
Not exactly - I didn't ask for enforcement, and the irritation usually arises from wanting the occasional secondary type - such as an enum - to be part of the public interface (thus forcing the type itself to be public, naturally) without either:
1) moving it into a separate file, for the simplest two value one liner
or
2) making it interior to the other class, with all the verbose lengthy names (and semantic implications) that this might entail.
I want clear and concise names, so I lean towards #1. And if an enumeration gets more shared between multiple classes, or collects enough values and comments to merit it, sure, I'll move it into it's own file. But forcing me to shove it in a separate file before I've even decided on the best name (meaning I need to rename the file repeatedly as well), and before I'm even certain I'll keep that specific enumeration, is just meaningless friction.
So have libmath depend on libsin, libcos, libtan and libsinh. People who want the kitchen-sink version can get it. People who want just one specific submodule can depend on that. What's not to like?
Having a full set of math functions isn't kitchen sink, it's the right level of granularity for a module. If I want math functions I really want them to all be written by the same author and kept in lock-step as a unit.
Why don't you want the whole module? Because it's bloat? Surely it's far less bloat than one submodule per function?
A lot of javascript is going to be deployed to a website.
Every line of code that isn't used is bytes you're sending to every user unnecessarily
> Surely it's far less bloat than one submodule per function?
How is that bloat? It's the same LoC whether it's 1000 modules or 1 after it's been compiled/minified
6 replies →
There's a cognitive load to every additional thing you need to know to use a module. At a certain point, added flexibility becomes more trouble than it's worth.
But there is no cognitive load to having hundreds of modules in a crazy tree of dependencies?
What's not to like? The fact that every one of those dependencies is an attack vector when one of those package's maintainers gets compromised / project hijacked / bought off by some black hat operator.
It's easier to keep an eye on a small number of trusted parties than 200 random strangers.
You thought this SNAFU was bad...
That has nothing to do with how granular packages are. The npm is broken as it allows such things. Look at this spectacular example of providing high security for users: https://www.npmjs.com/package/kik
Someone has to do this manually?! If the package is not popular, no one cares? What happens if I send them an email and provide the same package with the same API (not trademarked and MIT licensed) but break it a bit on every update?
No one knows.
1 reply →
> What's not to like?
That depends; in the case of mathematics libraries they have to be bundled together because of functional dependencies. It's either that or ignoring DRY principles -- which if you do that, you're ignoring the entire point of what breaking things into submodules is intended to do.
Separate interface and implementation. cos and sin might well use common underlying code (by depending on the same module), but they should expose orthogonal interfaces.
Oh, but javascript is dynamically typed, so it doesn't matter if your sin-module works with a different kind of arbitrary-precision decimal number module than your geolocation module, your cosine module or your openg-draw-triangle or opengl-draw-circle modules... /sarcasm
It's not sarcasm if it's true!
OT but apt to this community - That's what I thought, but after a lifetime of 'correcting' people, I looked up the definition of sarcasm. Turns out that the sarcastic-sounding truths I'd been deploying for decades were indeed by-the-book sarcasm rather than 'I can't believe it's not sarcasm'.
Key quote from brilliant word-talking-guy Henry Fowler: "Sarcasm does not necessarily involve irony [but it often does] ... The essence of sarcasm is the intention of giving pain by (ironical or other) bitter words."
So, we circle back to Node.js by way of pain and bitterness. Sounds about right.
You can get approximate feeling if you get rid of a library and don't use -lm but instead unpack it to separate .o files and link against each.