On Libraries, Reusability and Composability
Tags: programming.nodejs
There’s been a lot of brouhaha and schadenfreude regarding the recent failures in the NodeJS/NPM community.
Essentially the problem / schadenfreude focuses on two aspects:
A mutable package repository, allowing un-publishing of packages; this creates a number of practical & security problems.
NodeJS’s ethos of “micropackages”; the package at the focal point of the issue was an 11-line package, to left-pad a string.
Here’s a prominent post, Haney Codes’ “Have We Forgotten How to
Program?”,
which criticises NodeJS/NPM for having packages such as “isArray” or
“isPositiveInteger”; along with many other examples any non-Node programmer
will find absurd, horrifying, and hilarious.
My favourite bit:
if you cannot write a left-pad, is-positive-integer, or isArray function in 5 minutes flat (including the time you spend Googling), then you don’t actually know how to code. Hell, any of these would make a great code screening interview question to determine whether or not a candidate can code.
(Of course, some applicants [and self-described programmers!] fail Fizz-Buzz, so).
The NodeJS/NPM crowd in response point to this comment from sindresorhus (who, apparently, curates the very popular awesome-nodejs list), defending one-line packages. – The defense, essentially, is that these are versioned ‘snippets’, which provide a reusable abstraction.
Composition
A fun thing is. Haskell kids take this idea, and do kindof the opposite thing. Where NodeJS says “here’s a snippet we can use everywhere”, Haskell is more like “here’s a typeclass which is used everywhere”; e.g. semi-group or monoid or any of that.
– From the Category-Theory side of things, computation/programming is much
more about composition (than
simply naming/abstracting complexity, per se). e.g. FRP as a ‘fundamental’
solution to the problems of event-handling callbacks; FRP is basically
composing streams.
(“A monad is a monoid in the category of” blah blah blah – Monads are a way of
composing things).
I understand Haskell in particular runs into ‘cabal-hell’, ’cause of poor handling of transitive dependencies (it can’t have two copies of the same-package/same-version/different-dependency-version); and so when code is re-used so often, conflicts arise fairly often. (Moreover, since it’s compiled, it’s the ABIs conflicting, rather than a more forgiving dynamic-language interpretation like in Ruby or Python).
– So I wonder how “use a good type system” allows composition; & how that
compares/contrasts to Node’s.
Haskell’s typeclasses allow a benefit which you can’t really get easily in the
more mainstream languages.
Purpose of Libraries
Here’s a post from Benjamin Sandofsky, “When to Avoid
Libraries”, arguing for
prudence as to when to use external libraries; that programs should build their
own abstractions unless they shouldn’t. (Ah, the ol’ “it depends” really is the
answer to all topics in programming).
In comparison to the above, Sandofsky agrees that functions shouldn’t be
packages. Sandofsky argues (interestingly) that frameworks make a good starting
point, but that programmers should iterate towards proper abstractions/models
for fitting the problem domain they’re trying to solve. Sandofsky concedes that
a library should be used if a complicated task needs to be done, and you’re not
interested in the details.
Aside from saying “iterate towards a domain”, the other cute insights are “DRY code/libraries are quite difficult to debug”, and “if an abstraction leaks, no one cares which package is at fault in your program”.
Other Thoughts
I can kindof agree with the idea of “reusable snippets of code” being a good
thing. Usually the collections of snippets get called “Utils”. It’s common
enough on various Java answers to see answers of “Oh, just use Apache Commons
for that string encoding task”.
OCaml’s core library is infamously lacking in features.
– But since packaging systems aren’t more granular than ‘packages’/projects, I
don’t see that the above ethoses can reconcile.