To answer this question we should look at problems this kind of libraries solve. To get some ground I’ll illustrate them with code. That will be in python, but all the ideas are universal, so don’t be afraid.
A piece of entangled code
This messy piece of code was taken from a real project and slightly simplified:
1 2 3 4 5 6 7 8 9
There are several things entangled in here, but my point is that this could be written much shorter:
If it seems hard at first, then it’s okay. It involves some functions and control flow abstractions most probably new to you. Once you get used to it you’ll find that the latter variant is not only shorter, but is also simpler.
But let’s go on and clean some dirty dictionary:
1 2 3 4 5 6
Here we go through dictionary and clean its values by coercing them to
int. Or to
None if that is impossible. Cleaning input and ignoring malformed data is quite frequent task and yet it takes so much effort. This is the way I want to write that:
And it’s entirely possible with funcy. But let’s move to the next one.
This code checks if a sequence is ascending:
1 2 3 4 5 6 7 8
Ah, iterating over a sequence and keeping track of a previous element. How many times had you done that? There should be a function to abstract it:
And pairwise does exactly that. It enables us to iterate by sequence adjacent pairs. So we just need to check that all of them are ordered accordingly.
All these examples have one common property — red variants have more code. And more code:
- takes longer to write,
- takes longer to read,
- takes longer to debug,
- contains more bugs.
Obviously, underscore, funcy and friends help us write less code (at least in these three examples). But how do they do that?
Let’s take another look at the first example. It does three things in a single blob of code:
1 2 3 4 5 6 7 8 9
I highlighted every aspect of this code with separate color:
- image download (green),
- retries on fails (red),
- iteration through urls and result collection (blue).
As you can see, three colors are interleaved here. This hints that corresponding aspects are entangled. And by “entangled” I mean they can not be reused separately. Say we need retries on fails in some other place, we will probably end up copying the whole block and updating it somewhat. Not exactly the best practice “code reuse”.
If, on the other hand, we managed to separate reties then our code will look like:
1 2 3 4 5 6 7
Now red code is nicely grouped at the top. Green and blue are still mixed, but now they represent a pattern so common that most modern languages have a builtin function to handle that:
1 2 3 4 5
This last variant has some lovely traits: each part of a task at hand (downloading images) appear only once, the whole iteration aspect is handled with a single
map() call and retries are abstracted out into the retry function.
Extracting common behavior into a higher order functions is a first trick underscore and funcy use to make your life better.
Hiding low level
It’s time to go back to second example. I’ll throw away error handling to make snippets more even:
1 2 3 4 5
Now they are both one-liners, so how is first one better? Let’s identify every single distinct component of each code variant:
1 2 3 4 5
The second one looks like rainbow. But besides looking nice this means each time you write or read it you need to load all those components into your head, taking up all your cognitive resources. This is how first line is better.
That could be highlighted in even more obvious manner:
1 2 3 4
This way we can see that about a half of the second line is low-level details. And low-level mean you don’t need to know all those details to understand what’s going on.
Hiding low-level details is the second way such libraries make your life better.
Enriching our language
I’ll translate the last example into natural language:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Obviously, more code emits more text. Higher level code generates an explanation utilizing higher level abstractions. This way we can use bigger building blocks not only in coding, but in problem solving.
And this is the third way _ makes your life better.
All the things we came through are completely language independent. So there gotta be underscore for every language? Not quite, and more importantly a straight-forward port is not always a great idea: common behaviors to abstract vary per language and especially per application. The right approach would be to follow core ideas. Or look around if someone have already done that.
Here are some leads for you to take:
|Python||itertools, functools, funcy, toolz, fn.py|
P.P.S. Please stop commenting on Hacker News, a controversial penalty is killing this post. Use reddit thread instead. Sadly, HN is not a place for discussions anymore.