Having to implement range selection in lists in an application at work, I did a little survey of what is done, and was surprised that while the bases are the same for all lists, the more advanced selection modes vary a lot.
Note: by “list”, I mean the control displaying a list of items, not some kind of collection / data structure.
In our AngularJS application, we often display lists of things, in the form of rows of items, spawning several columns, each column showing some specific information from the item.
For this, we use the ag-Grid component.
Sometime, we need to select several items (lines) at once.
Surprisingly, ag-Grid support, out of the box, is quite lacunar: we can select several items, one by one, with Ctrl+click.
By default, we cannot even deselect an item! We have to activate an option for that.
And we cannot do a range selection…
Fortunately, the grid has a rather rich selection API, so it is quite easy to supply the missing feature.
And in an issue of the ag-Grid repository, somebody provided a base implementation of the range selection feature.
I was a bit surprised to see the implementation was a bit lacunar and doing some things in a surprising way. So I checked how it is done in a number of popular applications, and found out the implementations differed a lot! And that implementation was actually mimicking the behavior of Qt lists.
The applications I checked are:
select
tag, showing a list: tested on latest Firefox, Chrome and in IE9-11, on Windows.Reminder about the latter: it looks like
1 | <select name="select" multiple size=10> |
and can be tested in sites like jsFiddle.net.
Things all lists implement the same:
Shift+click: this is where things start to diverge! It always select the range of lines between the last click and the currently clicked row. But:
Ctrl+Shift+click is not universally implemented. Actually, I did a little survey in a programmer group on Google+, and with over 80 answers (OK, that’s not much), 20 % of them told they don’t know how to use Ctrl+Shift+click…
In Qt, it just works as Shift+click.
In Thunderbird, Firefox’ implementation of multiple select, and Chrome’s one too, it selects the range too, but it is added to the current selection. Just like the previous ones…
In Windows Explorer and multiple select in IE, it does that, too. But not only. If your last click (a Ctrl+click, necessarily) deselected an item, then the Ctrl+Shift+click deselects the range, instead of selecting it.
It is powerful and flexible!
I haven’t implemented yet, but I also checked the possibilities of selection with the keyboard: it is important for accessibility purpose, but it can be also useful in general.
When the list has the focus, all implementations move the selection with up & down arrows (UDA).
Likewise, all of them extends the selection from the current position with Shift+UDA.
Now, if you do Ctrl+UDA, WExp, Qt, Firefox and Thunderbird moves the focus, highlighting the current row (with focus) with a small border.
Chrome just deselects all and selects the target row, ie. it ignores the Ctrl modifier.
Curiously, IE behaves differently than WExp, for once: it keeps the selection unchanged, but moves the scrollbar (if any).
The implementations moving the focus then allow to select or deselect the current row with Ctrl+space. Or even just space in some cases.
Upon doing Ctrl+Shift+UDA, only Firefox extends the selection from the current position to the new one.
Curiously, Windows Explorer extends the selection to the new position.
Qt just moves the focus.
The implementation is rather simple, actually.
Note I give it as it was done, with the ag-Grid API, but in the main function, I abstracted away some of it, and the remainder is easy to understand, so the logic can be adapted to another API.
1 | // gridOptions are specific to ag-Grid. |
The compatibility layer.
1 | // Make the above code to work with current (v4) or previous (v3) version of ag-Grid. |
Here is an example of exclude
function definition:
1 | function excludeEditableCells(row) |
MobX is a library simplifying state management by using functional reactive principles.
It manages dependencies between values, somehow like a spreadsheet does with its cells: if the value of a cell changes, other cells depending on the first one can change too, in a cascading way.
The manual of MobX can be read at MobX, Simple, scalable state management.
It is a comprehensive guide, rather dense (worth reading again after experimenting with the library…) but clear and informative.
I used it in an AngularJS 1 application, to manage state of controllers (components), so in a rather local and isolated way.
That’s just one way of using it, which I explain below.
On my second AngularJS project (the third made in the company), we have some quite complex state management: we have a main view / controller showing a graph (nodes with relations), with a toolbar, whose buttons are disabled depending on user actions (changing the graph), data loading (fetching graph data), state in the data (is graph processed on server side), etc.
We also have two dialog boxes (one calling the other) with controls and buttons, also interacting, depending on user choices, state of / in data, etc.
We have to enable / disable these controls, and also set tooltips or messages explaining why they are disabled, so that user can do proper actions.
All this led to a rather complex state management, done in a classical way, with booleans and intermediary data structures, and functions called to update dependencies between these variables.
We managed this state with a bunch of booleans (is this control disabled?) set on the fly in a ad hoc way (uh, I need this flag, let’s create it there) on the scope (the controller instance), and by calling functions to show appropriate tooltips / messages in all places where the state changes.
Note: by “show tooltips / messages”, I actually mean “change their values”, as AngularJS’ binding is managing to display them.
At the end, the code was a bit messy, with state changing everywhere, forgotten cases, having to track usage to add the missing ones, sometime inconsistent or unclear usage, etc.
Then I saw a tweet mentioning MobX. I had a look at the site, and the description looked like a good fit to my problem.
A superficial look could lead to think it was made for React with an ES6 syntax, but reading on, I found they are actually framework-agnostic (React-specific stuff is separate) and totally compatible with ES5.
I use the “controller as” pattern in this Angular application, and I assign this
to the ctrl
variable at the start of the controllers.
So I refer to ctrl.xyz
for the controller variables, those visible from the related template.
I also have some variables with same lifetime than the controller, but not needing to be visible on the view. By convention, we name them ctrl._xyz
, the underscore meaning “private”…
If you don’t use “controller as” pattern (but you should!), you can mentally replace ctrl
with $scope
.
I tend to name functions that are traditionally anonymous, like iteratees of Lodash functions, callbacks defined in function calls, etc.
Ie. instead of doing _.map(circles, function (c) { return TWO_PI * c.radius; ];
I write _.map(circles, function __toPerimeter(c) { return TWO_PI * c.radius; ];
.
This is generally a good practice, as it shows the intent of the transformation, and these names can show up in stack traces in debuggers, etc.
It is even more important with MobX, because it keeps track of these names and this allows an easier debugging of its mechanisms.
I use a double underscore as prefix to help distinguishing these small functions from the others when my IDE (Atom) lists them.
I have two objects on the controller, related to the view, ie. referenced in the template:
ctrl.viewModel
contains the models of form inputs (text input, radio-buttons, check boxes, select / drop-down, etc.), referenced by ng-model
directives. Ie. they display their current value and can be changed by the user.ctrl.view
contains the read-only view values, used in the template to display these values.Between them, I can have some intermediary objects, eg. ctrl._model
for data structures, ctrl._internal
for flags, etc.
These objects, and ctrl.viewModel
, are observable by wrapping them in a mobx.observable
call.
MobX transforms (recursively) each field (property) of these objects to observable values: that’s simpler than calling observable
on each field.
If you use a more OOP approach, as described in the docs, you might want finer control on how some fields are observable and some are not.
Here, I just use these plain objects to group together observable values.
These observables can be simple values set by the code (string, number, boolean); or arrays (each value is observed), or objects (each property is observed). It is, by default, recursive: an array of objects has each field observed.
Observable values are replaced by MobX by a pair of accessors, ES5’s getters and setters, or specialized objects, like ObservableArray, mimicking the behavior of real arrays, but observing each entry and operation (like splice
).
If the property of an observable object is assigned a new value, MobX makes it observable too.
It doesn’t do it for added properties, so they must be declared from the start, which is a good thing: no more variables added “on the fly” in the middle of the code.
If you use an object is a map (values indexed by string keys), MobX provides an observable map to get you covered.
In some cases, I observe arrays to watch insertions / deletions: I don’t change the objects it has inside, which generally doesn’t change anyway. To improve performances (and memory consumption) and ease debugging, I use the asFlat
modifier.
The observed values can be also be functions without parameters: they are called when the field is read.
The function acts as a getter, and can compute a value from other values.
If some of these values are observables, MobX invokes their getter, which in turn can invoke other getters, etc.
Instead of a direct computation from other values, the function can call a transformer, created via… mobx.createTransformer
.
A transformer takes one (and only one) observable value as entry, and must return a new value computed from the parameter, and possibly from other observable values.
As we use properties from observables, we invoke their getters. MobX then keeps track of these sub-observable objects.
When a setter is invoked on one (or more) these objects, MobX knows it has to recompute the result of the transformer.
Otherwise, since input hasn’t changed, it knows output didn’t change either†, so it can return the previous value, said to be memoized. This results in a much faster response, particularly if the operation iterates on large collections.
† Obviously, the function must be deterministic (pure, as functional programming aficionados say): for a given input, it always returns the same output. Don’t put random()
, getTime()
or I/O result in there!
The above is an over-simplified description how MobX works. The exact mechanism is described in the article Becoming fully reactive: an in-depth explanation of MobX. MobX has optimizations making all this working very fast…
Beside, as the core mechanism is based on accessors, it is intrinsically static: no need to scan all the observed variables / entries / properties regularly to check if there is a change, the sole fact to get or set a value is enough to trigger the mechanism. The other advantage is synchronicity and atomicity: changes don’t have to wait for a “digest” cycle, and all changes are applied at once: no inconsistent intermediary state.
Back to my implementation: ctrl.view
is not an observable. It is already watched / bound by Angular, and I prefer to keep it “static”, assigning new values if their dependencies change.
For this, I use mobx.autorun
which is used to do side-effects when an observable value inside it changes.
My side-effects here are to assign a new value to ctrl.view
properties… I do that only for computation-intensive value evaluations (extracting data from collections, etc.), using transformers to memoize the results.
Actually, it corresponds to described side effects, like disabling a control, selecting an entry in a list, etc. Because it is Angular, instead of calling control.setEnabled(true / false)
or combo.select(index)
, we assign the values to bound variables: ctrl.control.enabled = true / false
or combo.selection = combo.model[index]
. But basically, that’s the same thing.
As createTransformer
is a bit heavy-handed (needs some boilerplate code, accepts only one parameter), for lighter evaluations (no iterations) I use the ES5 getter syntax, function evaluated each time the property is read: var o = { get foo() { return someValue * 2; } }; var x = o.foo; // Calls the getter and returns twice someValue
I formerly used mobx.computed
for that, but I am not sure if it brings any advantage here…
In other words, I went from
ctrl.view =
{
saveDisabled: mobx.computed(function saveDisabled()
{
return ctrl._internal.noItems || !ctrl._internal.hasCommonStuff || ctrl._internal.isStarted;
}),
};
to
ctrl.view =
{
get saveDisabled()
{
return ctrl._internal.noItems || !ctrl._internal.hasCommonStuff || ctrl._internal.isStarted;
},
};
Note: transformations must be used inside a reaction like @observer
or autorun
. They also work if they are part of the dependency graph pulled from an autorun
, as done above (view depending on observable objects).
Maybe the computed
version allows such triggering, if needed.
That’s a general advice in computing: instead of using null
, use a “null object”, a real “inert” object . Like an empty object {}
, an empty array []
or similar.
MobX will throw when you assign null
to an observable, and will tell you that you have an infinite loop in your dependencies, so these null objects are better here.
It is a special object mimicking JS arrays, but observing each entry and watching each operation. It is a bit annoying to examine when debugging, but that’s a minor issue.
One problem is that some libraries will choke on this “false” array. For example, [Lodash]/Programming/Using-Lodash-for-fun-and-profit/)'s _.clone
or _.cloneDeep
fails to work on it.
For the former, you can do observableArray.slice(0)
which makes a plain array copy of the array. But the objects inside are still those observed in the original array, and thus are still observable.
In my case, I observed the array only to see if it is not empty, which is a bit overkill…
ctrl._model = mobx.observable(
{
stuffList: [],
});
Easy way out: I tell MobX not to observe the objects themselves.
ctrl._model = mobx.observable(
{
stuffList: mobx.asFlat([]), // Observe array, not objects inside it
});
Assigning later the real array to stuffList
preserved the asFlat
modifier.
Basically, if you are interested in changes in the array (add / remove items), but not in the objects inside them (which might not change at all!), asFlat
is good to use (can even improve performance / reduce memory usage, which doesn’t hurt…).
It might seem strange to use MobX with AngularJS, whereas the latter can $watch
value changes or invoke functions from the template.
MobX has two advantages here:
This avoids explicit $watch
(I never use them in controllers anyway), evaluation of functions in templates (like ng-disabled="vm.isFooDisabled()"
) or manual calls to ctrl.updateState()
in the controller code, which can be forgotten or done too much.
MobX allows to describe a graph of dependencies, and to ensure it is always up to date.
For complex changes, like changing several values in an array, we can also use transactions, minimizing the recalculations by batching them and computing the result at the end.
MobX is a mature library, used in large applications, and it proved to be very efficient.
It is flexible: some applications use it to implement the Flux architecture, centralizing all data in one or more data stores, shared among the components; and myself used it locally, in only some controllers, to do a little part of the UI logic.
In other words, it is not an “in your face, do it as I said” library that imposes its vision of the world (centralized, immutable, with flat data, and so on). It works the way you are familiar with, and it is easy to use for most users, using mutable data, invisible accessors, etc.
The rules are defined in http://git-scm.com/docs/gitignore
I won’t repeat them here, nor test them all. I will only explore some corner cases, not well explained in the said document…
List all candidates to tracking with git status -u
.
x means ‘shown by status’, empty means ‘ignored’.
foo |
/foo |
foo/ |
f* |
f*/ |
*.y |
foo/c.x |
foo/*.y |
**/foo/*.y |
|
---|---|---|---|---|---|---|---|---|---|
bar/b.y |
x | x | x | x | x | x | x | ? | |
bar/foo/a.y |
x | x | x? | ||||||
bar/foo/c.x |
x | x | ? | x | ? | ||||
bar/foo/d.y |
x | x | x? | ||||||
baz/f.y |
x | x | x | x | x | x | ? | ||
baz/foo |
x | x | x | x | x | x | ? | ||
baz/nf.x |
x | x | x | x | x | x | x | x | x |
foo/a.x |
x | x | x | x | |||||
foo/b.y |
x | ||||||||
foo/c.x |
x | x | x |
A bit surprised by the foo/c.x
and foo/*.y
patterns, I can’t see in the doc where they say that a pattern with slash and joker is anchored to top.
The last one is probably barely legal. One should keep things simple…
Now, let’s see the combination of exclude and re-include.
foo |
bar (1) |
bar |
bar/* |
bar/foo/* |
bar/foo/* |
|
---|---|---|---|---|---|---|
!bar/foo |
!bar/foo |
!bar/foo/* |
!bar/foo |
!bar/foo/c.x |
!bar/foo/*.y |
|
bar/b.y |
x | x? | x | x | ||
bar/foo/a.y |
x | x | x | |||
bar/foo/c.x |
x | x | x | |||
bar/foo/d.y |
x | x | x | |||
baz/f.y |
x | x | x | x | x | x |
baz/foo |
x | x | x | x | x | |
baz/nf.x |
x | x | x | x | x | x |
foo/a.x |
x | x | x | x | ||
foo/b.y |
x | x | x | x | ||
foo/c.x |
x | x | x | x |
(1) and /bar
and bar/
I was confused by the re-include behavior, the post at http://stackoverflow.com/questions/2820255/how-do-negated-patterns-work-in-gitignore clarified the issue.
We need to add a joker to the exclude path so that re-include paths are examined.
Recursively:
bar/* |
bar/foo/* |
bar/foo/* |
bar/* |
bar/foo/* |
|
---|---|---|---|---|---|
!bar/foo |
!bar/foo/c.x |
!bar/foo/*.y (2) |
!bar/foo |
!bar/foo/*.y |
|
bar/foo/* |
!bar/foo/zab/ |
||||
!bar/foo/zab |
bar/foo/zab/* |
||||
!bar/foo/zab/*.y |
|||||
bar/b.y |
x | x | x | ||
bar/foo/a.y |
x | x | x | ||
bar/foo/c.x |
x | x | |||
bar/foo/d.y |
x | x | x | ||
bar/foo/zab/e.x |
x | x | x | ||
bar/foo/zab/f.y |
x | x | x |
(2) and !bar/foo/**/*.y
and !bar/foo/zab/*.y
Most Android keyboards suffer from the same flaw: they mimic traditional typewriter / computer keyboards.
The (only?) advantage is that most users are familiar with it.
The inconveniences are numerous:
I thought of a squarish / circular design around a central point, where letters surrounding it would change to make more accessible the letters more likely to follow the previous ones (for a given dictionary).
Idea with a number of caveats, like not relying on muscle memory and needing to know the context in the edited text.
So I searched alternatives (they are numerous!), tried some (disliked swipe-based ones, liked Hacker’s Keyboard in its classical category) and finally stumbled upon MessagEase.
I felt it was the right one, even if I expected some learning curve… It was close of my idea, but with a fixed layout (for a given language), and it makes the most common letters easier to type than the rarest ones.
Its square design make it look huge, but in practice, with its 4x4 keys, it is not much bigger than a four row traditional keyboard. And you can easily adjust its size to fit your taste / capabilities.
It is very flexible, with tons of settings to adjust finely the look and feel of the keyboard, to adapt it to the needs and tastes of the users. Yet, it has sensible defaults, so it is good out of the box.
The main area of the keyboard is a 3x3 square of keys. Each key (sub-square) has the nine most used letters of the chosen language (aniuortes for French, not far for English). They are typed with a simple tap on them.
The four next most used letters (hbdc) are obtained by sweeping from the central square to a side square. For the next four, the move is from the center to the corners. Then come the reverse moves, from sides or corners to center, providing the eight next letters. The last letter is obtained with a sweep between two sides. Other side to side or to corner moves provide accented letters and punctuation signs.
With some logic making them easier to memorize: lower signs (,.:;) are at the bottom of the keyboard while higher ones (`^´) are at the top, and the move follows the direction of the sign.
Numbers are obtained by a special keyboard (or a long press).
Special moves allow to easily reach capital letters: a circular move over the main letters, and a sweep and back for the others. Plus autocapitalization at the beginning of sentences.
The keyboard also provides very convenient special keys: you can cut, copy or paste with a single sweep. A side sweep on the space bar moves the caret by one character. Sweep and back to move by a whole word.
Tap on the backspace to delete the char on left of caret. Sweep to left and back to delete from caret to start of word. Sweep to right to delete the char on right of caret (idem Delete key), sweep and back to right to delete to end of word.
Some characters are obtained via a combination key: oe plus the key gives œ; << and the key gives « (French quote). Etc. `^´ keys are automatically merged if the previous letter accepts accents (eg. E and ´ gives É). Backspace cancels the merging.
There are other shortcuts, like defining sentences to enter with a special gesture, etc.
You can have an area displaying suggesting words completing the one you are typing. For your selected language, of course. You have to install the languages separately.
These are only suggestions, you can ignore them or tap on one to autocomplete the current word. No stupid autocorrection causing so often confusion, embarrassment and fun… at your depends! :-)
The keyboard layout is, by default, adapted to the chosen language, depending on the most often found letters of the language. But as I type as much English than French, I chose to use the same keyboard for both, because learning to use efficiently the keyboard involves memorizing the letter placements and using muscle memory. So having two keyboard layouts would be rather couter-productive (unless you have a better brain than mine…), at least for similar languages (eg. latin-based). Of course, English vs. Arabic, for example, is a different problem.
I chose the French keyboard, as it has the accents I need. I don’t mind to have a less optimal character layout for English, the frequency of letters isn’t so different it makes a huge difference… Of course, I have the two dictionaries (for autocompletion) and I appreciate the ease of switching them and the different color coding.
The keyboard has been designed by Edideas.
When I discovered MessagEase, I thought: “Ah! That’s the one I was looking for!”…
It is an ideal keyboard both for typing text and for programmers.
With traditional keyboards, I despised to have to hit three keys to enter a capital, a number or a punctuation sign. And editing capabilities were non-existent, beside trying to tap the caret between two letters.
Despite the initial learning curve, I love to be able to move the caret, delete the char or word on right, delete the previous word, paste quickly…
And most characters used in programming are just a sweep away: {} $ [] &| etc. Even the tab character is readily available!
Idem for accented letters, always just a sweep away.
The app is free, without ads. They ask for support, which is well deserved. They also give a free game with various goals, allowing to train, measuring speed, errors and progresses. After some training, I reached a decent speed (much higher than with default keyboard). After some months of using this keyboard (typing articles such as this one in public transports on my tiny old phone), I tried the game again. And I found out I outperformed all my previous results by a large margin! Practice make it perfect. Note that pure speed is really a goal in itself (except for the challenge, of course): I don’t type very fast (still reaching the top level of the games), but I don’t care as I frequently stop to think what I will type next… The real goal is to let the fingers to find the letter gestures by themselves, to avoid hunt-and-type each letter. Ideally, one should be able to type without even seeing the letters on the keyboard. MessagEase has even a “blind” mode, with a grid without letters…
Note: I typed most of the articles of this site on my small phone (a Samsung Galaxy Ace 2) with this keyboard. I doubt I would have managed to do this with a classical keyboard!
Aka. nitpicking… Aka. personal message / remarks to the MessagEase team!
Seen:
indecated
commends
more then two keys
“A hackable text editor for the 21st Century”
I came to Atom with a prejudice…
The long closed-source / beta period, the fact the “hackable” editor isn’t coded in JavaScript but in CoffeeScript, the need to add plugins for everything (according to some reviews), the reported slowness and memory hungriness, made me to hesitate to try it… I was also a happy user of Adobe Brackets, with no compelling need to change.
But, Brackets accumulated a number of little annoyances, making me wanting to try other editors.
The test of Visual Studio Code was brief, as it didn’t have the base (semi-advanced if you want) features I use all the time: drag’n’drop of code, and column selection.
So I tested Atom, with a critical eye. Spoiler: I was seduced, and it becomes my favorite Web IDE…
That’s the problem with information found on Internet: it quickly becomes stale…
The Atom team improved memory consumption, have an eye on responsiveness (I was impressed by their TimeCop package, and the fact they report for each package the time they add to loading), ship lot of plugins with the base editor: delegating to packages is part of their vision of modularity, but they made official packages for the most essential features, and you don’t need to hunt every package implementing base features, saving time.
I appreciate the clean interface and the attention to details they have bring to the project.
I downloaded the installer. It is big! Of the IDEs I tried, it is among the biggest:
Name | Version | Size |
---|---|---|
Brackets | 1.5 | 37 MB |
Visual Studio Code | 0.10.3 | 43 MB |
Light Table | 0.7.2 | 48 MB |
Atom | 1.3.1 | 88 MB |
WebStorm | 9 | 140MB |
Really heavy for a so-called “text editor”.
I installed Atom (v. 1.3.1 on Windows 7 with 8 GB of memory).
It doesn’t ask where to install, it goes arbitrarily at C:\Users\<user name>\AppData\Local\atom
. I would prefer to install it beside my other programs, to locale it easily. And I would appreciate it asks me before installing something on the desktop (I have no icons there!) or on the start menu (I no longer care about this one, but still…).
It is a bit “all over the place”, as it also has a C:\Users\<user name>\AppData\Roaming\Atom
folder (for cache?), and a C:\Users\<user name>\.atom folder
(settings, packages…).
Note on icons: in the Quick Start menu, and in the context menu of files, they use the path to the .exe file to give the icon. But this path changes on every auto-update! And they don’t update it… Simple fix: replace this path with %USERPROFILE%\AppData\Local\atom\app.ico which is provided with the editor…
I opened it, it starts rather quickly.
It has a dark UI, which I don’t like, but I quickly found where to change this to the default light theme. Good point than several themes are bundled by default: no need to hunt for them, at least at start. The default light UI theme is rather nice. I also chose the One Light syntax theme among four. A bit too pale (low contrast) for my taste, I will eventually see if I can tweak it.
As I feared (I searched a bit before, given the Visual Studio Code deception), drag’n’drop of code isn’t available out of the box. Actually, I saw several plugins activating it. That’s one of the problems with this editor: there is a plethora of plugins, which can be nice, but it is confusing to choose one… And I still think such feature should be built-in in the editor.
Same for column (rectangular) selections! Later, I found out Atom supports out of the box column selection (Ctrl+Alt+Up / Down, extend only, allows rectangular selection with Shift+Left / Right) and multiple carets (Ctrl+click).
I find a bit unsettling to have to install lot of plugins to get base editor features. I have the same issue with Brackets, but it has at least these two features built-in (but D’n’D must be activated by a setting).
That said, I found several features out of the box for which I had to install a plugin in Brackets:
Two features I mentioned in the above article are also missing from Atom:
OK, I switched to Atom to type this text (in Markdown format). Good surprise: it has spell checking enabled out of the box. And it can suggest words, although I am not sure of the logic of suggestions (scanning existing buffers, apparently; nice point: it is not obtrusive). Little bug (or unwanted feature ;-) – I see a misspelled word in the text (red underline), I right-click directly on it, and choose Correct spelling. It does nothing… I found out I have to left-click first on the word to get spelling suggestions.
I see no way to add words to a user dictionary (there is an issue for that, and another for other language dictionaries).
The Welcome Guide is nice, I just saw a way to customize the styling. Although it lacks concrete examples: eg. how do I change the color of titles in Markdown? (I avoid reddish colors, reserved to mark errors). Ah, I suppose I have to study an existing style, like https://github.com/atom/atom-light-syntax/blob/master/index.less
And I can see the actual styles by showing the developer tools (View > Developer > Toggle Developer Tools) and using it like Chrome DevTools.
Bug: I have Choose a Theme and Customize the Styling opened in the Welcome Guide. I can’t scroll back above Install a Package. I have to close one to see the top!
And I can’t scroll the list of packages (installed, to install, etc.) with keys (up / down / page up / page down).
Another bug or unwanted feature: it ensures there is an empty line at the end of a file when saving it, which can be nice (I generally want that), but if I have several lines, it removes the extra ones (which is annoying in a text file, because I want to keep my future paragraph empty lines). Will see if I can disable that.
Undo coalescing is a bit strange: when I type a word, then undo, it removes parts of the word, successively, instead of whole word.
Apparently, it coalesces when there is a pause in typing. Half a good idea, bad for slow typists (not sure if this can be tweaked), not consistent. I prefer Scintilla’s coalescing, based on keys: when an move key (eg. arrow) is typed, or when clicking somewhere.
Atom lacks a nice feature of Brackets: in an HTML file, Brackets can auto-complete the path of a resource (script, CSS file, etc.). Found a plugin for that, of course… Although it is slightly less convenient.
Apparently, Atom has only “duplicate-lines”, not “duplicate-selection”, alas. Sometime, I want to duplicate the selection inside the same line, eg. to quickly add an additional parameter with its type.
Good idea: if I type something, the buffer is marked as dirty, of course. If I edit back to the initial state (eg. type a char, then Backspace), instead of using Undo, it sees the file as pristine again (as Git would do…).
Annoyance: I can hit Ctrl+F in the Settings > Package page (for example), but it doesn’t find anything there (can be convenient to find a given package by something else than its name).
Command palette: good idea to highlight searched terms, but on light theme, I get light gray highlight, nearly unreadable on very light gray background… [EDIT] They fixed that!
Bug: column selection doesn’t skip wrapped part of lines. Ie. if lines are wrapped, and if we extend a column selection beyond a wrapped line, the wrapped part is also taking the selection.
https://github.com/atom/atom/issues/10234
Bad idea: I can do a column selection with Ctrl+Alt+Up / Down. BUT, they thought it was a good idea to extend in both directions… So there is no way to reduce the selection if we overshot! In general, I don’t start in the middle of my selection, I am either at the top or bottom line, then I go from there. So I expect, if I go in the reverse direction (of the initial choice), to reduce the selection.
Bug, or annoyance (as I am used to a different behavior for other editors): if I hit Ctrl+Delete at the start of an indented line, I expect the editor to remove only the indentation characters, ie. to remove spaces or tabs up to the first non-whitespace character. But it eats also the beginning of the significant line, ie. the first word or the first symbols.
Relevant issue with a workaround: https://github.com/atom/atom/issues/4026
1 | 'atom-workspace atom-text-editor': |
In general, I am annoyed by a different behavior on managing whitespace. Example: when I delete a word, I am used* to see the following (or preceding) space(s) to be removed as well. So I can delete several words quickly. Here, I have to delete the intermediary spaces as well.
I also would be appreciate that Home key sends to start of line (column 0) instead of start of text (after indentation). That’s the things I can set up in SciTE…
Auto-indent is broken. Particularly when I paste a code snippet: it indents the already indented code, resulting with double indenting!
I just activate the “keep indentation level” setting, which some time misses a level…
https://discuss.atom.io/t/normalize-indent-on-paste-doesnt-work-as-expected/3503/12
Annoyance: there is no way to set the setting tabs vs. spaces per buffer.
https://discuss.atom.io/t/sometimes-tab-inserts-spaces-even-with-soft-tabs-off/3976/37
Minor quibble: title and closing cross in inactive tabs are one or two pixels too low (one pixel lower than the active tab, at least). It “hurts” my sense of tidiness… :-)
Overall it is a good editor. It is reasonably fast for my usage (I don’t open larges files, a known weakness of Atom).
Well, almost. When Windows swaps out its memory (going to sleep), it is very slow to wake up.
More annoying, it often displays a message on slowness (“Editor not responding”), I have to hit the button “Keep Waiting” to continue.
https://github.com/atom/atom/issues/7275
Even more annoying: the dialog stops everything. The same dialog in Chrome spontaneously disappears when the slowness is no longer detected. Here, the editor waits for clicking on Keep Waiting before resuming!
https://discuss.atom.io/t/refinements-to-the-not-responding-keep-waiting-dialog/24375
The minor issues mentioned above doesn’t prevent from using it: the good parts currently balance the bad ones.
I will still recommend it. :-)
Good point: once a plugin / package is installed, no need to restart Atom to get it working.
Note: list can be incomplete, and version numbers are indicative only: I won’t update them on each package update!
You can find a more complete list (when I back up my settings with sync-settings) at https://gist.github.com/PhiLhoSoft/b61b3e3d13ebd802bb08
I found an interesting list at the https://github.com/mehcode/awesome-atom repository. I added some packages like duplicate-line-or-selection after reading it…
Sublime Style Column Selection
Allow column selection with mouse.
1.3.0 by bigfive
https://atom.io/packages/Sublime-Style-Column-Selection
https://github.com/bigfive/atom-sublime-select
simple-drag-drop-text
Enable classical drag’n’drop of text, with move (by default) or copy (with Ctrl).
0.3.0 by mark-hahn
https://atom.io/packages/simple-drag-drop-text
https://github.com/mark-hahn/simple-drag-drop-text
keyboard-localization
Must have if you have a non-US keyboard; eg. a French one: I can’t type } nor ] because I have to use AltGr (ie. RightCtrl+RightAlt) to reach them, and this makes interference with some Ctrl+Alt shortcuts.
1.4.112 by andischerer
https://atom.io/packages/keyboard-localization
https://github.com/andischerer/atom-keyboard-localization
duplicate-line-or-selection
Something that worries me in Atom from the start, as I have it in SciTE: we can duplicate the current line, but not the selection. This package fixes this problem.
0.5.0 by nick
https://atom.io/packages/duplicate-line-or-selection
https://github.com/nick/duplicate-line-or-selection
tree-view-open-files
Shows opened files above the tree-view. Better than tabs when lot of files are opened.
0.3.0 by postcasio
https://atom.io/packages/tree-view-open-files
https://github.com/postcasio/tree-view-open-files
Atom Tabs Expos�
Quick tab overview of open files. Similar to Mac OSX Expos� / Mission Control, Firefox Tab Group, Safari and Chrome Tab Overview, etc.
0.11.1 by mrodalgaard
https://atom.io/packages/expose
https://github.com/mrodalgaard/atom-expose
minimap
Visual miniature representation of the content of the editor. Allows quick navigation.
4.18.0 by atom-minimap
https://atom.io/packages/minimap
https://github.com/atom-minimap/minimap
highlight-selected
When a selection is done, corresponding words (must be whole words) in the same buffer are highlighted as well.
0.11.1 by richrace
https://atom.io/packages/highlight-selected
https://github.com/richrace/highlight-selected
minimap-highlight-selected
Shows highlights by the previous package in the minimap.
4.3.1 by atom-minimap
https://atom.io/packages/minimap-highlight-selected
https://github.com/atom-minimap/minimap-highlight-selected
autocomplete-paths
Autocompletion for paths in the project, according to file system. With patch https://github.com/atom-community/autocomplete-paths/pull/51/files applied manually…
1.0.2 by atom-community
https://atom.io/packages/autocomplete-paths
https://github.com/atom-community/autocomplete-paths
autoclose-html
Automatically close HTML tags.
0.19.0 by mattberkowitz
https://atom.io/packages/autoclose-html
https://github.com/mattberkowitz/autoclose-html
toggle-quotes
Keyboard shortcut to switch between single and double quotes on the string the caret is in.
1.0.0 by atom
https://atom.io/packages/toggle-quotes
https://github.com/atom/toggle-quotes
atom-ternjs
JavaScript code intelligence
0.13.2 by tststs
https://atom.io/packages/atom-ternjs
https://github.com/tststs/atom-ternjs
linter
Engine allowing to display lint information in real time in the editor. Need plugins for various languages.
1.11.3 by atom-community
https://atom.io/packages/linter
https://github.com/atom-community/linter
linter-eslint
Linter for JavaScript.
5.2.6 by AtomLinter
https://atom.io/packages/linter-eslint
https://github.com/AtomLinter/linter-eslint
linter-htmlhint
Linter for HTML.
0.2.1 by AtomLinter
https://atom.io/packages/linter-htmlhint
https://github.com/AtomLinter/linter-htmlhint
linter-stylelint
Linter for CSS and Sass.
1.9.1 by AtomLinter
https://atom.io/packages/linter-stylelint
https://github.com/AtomLinter/linter-stylelint
minimap-linter
Display linter markers on minimap.
1.1.1 by AtomLinter
https://atom.io/packages/minimap-linter
https://github.com/AtomLinter/atom-minimap-linter
pigments
Shows colors from various CSS notations (including named and computed colors). I prefer to display them as circles after the color definition.
0.19.3 by abe33
https://atom.io/packages/pigments
https://github.com/abe33/atom-pigments
atom-beautify
Beaufity code, according to given rules. Not for my code (I always format as I want) but for foreign code pasted in mine…
0.28.19 by Glavin001
https://atom.io/packages/atom-beautify
https://github.com/Glavin001/atom-beautify
file-icons
Add icons (depending on file type) in the tree view on the left.
1.6.13 by DanBrooker
https://atom.io/packages/file-icons
https://github.com/DanBrooker/file-icons
atom-easy-jsdoc
JSDoc generation.
4.2.0 by tgandrews
https://atom.io/packages/atom-easy-jsdoc
https://github.com/tgandrews/atom-easy-jsdoc
language-ejs
EJS template support, to cleanly edit the Hexo theme I chose. Apparently only syntax highlighting support…
0.2.0 by darron
https://atom.io/packages/language-ejs
https://github.com/darron/language-ejs
sync-settings
Synchronization of Atom settings across computers, using a Gist to keep track of history of various files.
0.6.0 by Hackafe
https://atom.io/packages/sync-settings
https://github.com/Hackafe/atom-sync-settings
tree-ignore
Allows to ignore paths in the project, hiding them in the tree-view.
0.3.1 by leny
https://atom.io/packages/atom-tree-ignore
https://github.com/leny/atom-tree-ignore
rest-client
Simple Rest client to generate requests to servers. Complement of RestClient on Firefox, Postman in Chrome.
0.5.0 by ddavison
https://atom.io/packages/rest-client
https://github.com/ddavison/rest-client
markdown-mindmap
Shows headings of a Markdown text as a mindmap, allows to navigate.
0.2.4 by dundalek
https://atom.io/packages/markdown-mindmap
https://github.com/dundalek/atom-markdown-mindmap
atom-perforce
Support of the Perforce VCS (I use it at work). Has problems on Windows, I fixed (some of) them.
1.7.0 by mattsawyer77
https://atom.io/packages/atom-perforce
https://github.com/mattsawyer77/atom-perforce
project-plus
Simply awesome project management in Atom.
0.9.0 by mehcode
https://atom.io/packages/project-plus
https://github.com/mehcode/atom-project-plus
string-looper
Change word case, number value and loop between keywords with the arrows of your keyboard.
0.1.2 by leny
https://atom.io/packages/string-looper
https://github.com/leny/atom-string-looper
test-jumper
To jump between main file and its unit test. Not used because it can’t seem to fit in our project layout… Still interesting.
0.5.0 by danielcooper
https://atom.io/packages/test-jumper
https://github.com/danielcooper/test-jumper
git-time-machine
Allows you to travel back in time! It shows visual plot of commits to the current file over time and you can click on it on the timeplot or hover over the plot and see all of the commits for a time range.
1.3.0 by littlebee
https://atom.io/packages/git-time-machine
https://github.com/littlebee/git-time-machine
merge-conflicts
Resolve git conflicts within Atom
1.4.2 by smashwilson
https://atom.io/packages/merge-conflicts
https://github.com/smashwilson/merge-conflicts
refactor
Allows refactoring (basically, renaming of identifiers in context). Needs plugins per language.
0.6.0 by hax
https://atom.io/packages/refactor
https://github.com/hax/refactor
js-refactor
Refactor plugin for JS (only rename variables and parameters).
0.6.0 by hax
https://atom.io/packages/js-refactor
https://github.com/hax/js-refactor
gl-light-syntax
2.1.1 by gouvinb
https://atom.io/themes/gl-light-syntax
https://github.com/gouvinb/gl-light-syntax/tree/master/styles/languages
naturerainbow-light-syntax
0.1.0 by fthiagogv
https://atom.io/themes/naturerainbow-light-syntax
https://github.com/fthiagogv/naturerainbow-light-syntax
So I want to write some new technical articles (like this one…) on my latest discoveries.
My previous host keeps my old site but has cut my FTP access, so I cannot update or add new stuff.
I found out that GitHub Pages might be a good free host for technical content, but it won’t allow a blog / CMS software (like WordPress) to run on it.
Of course, it has Jekyll, but I wasn’t seduced: the syntax is so-so (for me), it is a Ruby tool (slow and I have no control over it), etc.
But it was a hint: no need for a full fledged blog software generating pages from scratch on each user request: these pages are mostly static so they can be coded once and for all, like I did in the previous century…
The most dynamic parts are the back-office, which I can live without, and managing user comments (with spam control). For the latter, I saw most solutions just use Disqus, a cross-platform comment service relying only on JS (and the related site / cloud host), so needing no server-side script or database. And I already use it (as a commenter), so it looks like the way to go.
So I need something more flexible, and / or something I could hack into my needs.
A quick research shown, via two sites (Top Open-Source Static Site Generators - StaticGen and Static Site Generators) that there is literally a ton of static site generator softwares.
Note / strong hint for those making such software: if you don’t have a site to show of| what your tool can do, you are doing it wrong. No need to pay for a domain name, you can host it on a free site like GitHub.io or similar.
I first tried Hugo, which I discovered earlier: it has a nice site, which is an important point for this kind of software… And a decent doc. And native binary is a promise of speed.
But I quickly hit a limitation: the Markdown parser is quite rigid and not adapted to my needs. And since 1) I don’t know Go, 2) I don’t have much time for deep hacks, I want something working quickly, I looked elsewhere (it was a “no Go”…).
I could use a generator written in Lua, which is a language I know and appreciate, but they seem quite confidential, used by the author and a dozen of users (guestimate…), so I feared some issues… (or too much work to adapt to my needs).
There are also PHP generators: it feels odd to use this language for off-line generation… I can understand they appeal to those familiar with the language.
I can hack a Java-based generator, but I feared some heaviness.
I finally chose to explore JavaScript-based generators (via Node.js): I know well the language, and I know there is a rich ecosystem of Web-oriented tools.
Another interesting generator is Metalsmith: it has a very small core, and delegate all tasks to plugins which it orchestrates. It results in flexibility: it can also generate eBooks, etc.
But, I thought, it looked like yet another build tool, like Webpack, Brunch or Gulp, only with more specialized plugins.
I know web tools, I know how to transform Markdown to HTML, Sass to CSS, etc. I can surely leverage an existing build tool to do that, with even more flexibility. But as I haven’t much time to spend on writing these tools (finding plugins, chaining the tasks into something sensible), I searched some kind of existing “glue”.
I first tried Antwar which is based on Webpack and React.
It has a good site with a tutorial, and an interesting flexibility.
But I ran into some issues, like a rigid Markdown parser, so I looked elsewhere.
Then I tried AkashaCMS, still based on Webpack. The site is less sexy, but full of good information. It uses the Markdown-it parser, which is a good choice as it is very flexible.
I have to write my own template, which is not a real problem, particularly if I use x as a starting point.
But, out of the box, it uses the quite outdated EJS template system: the code is still in Google Code! The forum hadn’t a new message in months. And the syntax is ugly and heavy, reminds of JSP / ASP, and includes JS code to do its stuff.
Plus it uses Less to generate CSS, while I prefer Sass.
Again, I can replace these parts if I want, the author explains how to change a renderer, but again I wanted something faster to use.
I skip over other generators I saw, like Waffel (based on Brunch), which seems promising, or Gatsby also interesting, based also on React.js, but written in CoffeeScript.
Finally, I came back to Hexo, which I looked at several times before…
That’s one of the tools with a nice site, good tutorials, and lot of support: it lists lot of plugins and themes, so it is easy to get started.
Alas, lot (if not all) of these themes are written in EJS (why is it so popular?) and Less…
These were the initial choices of Hexo. Since v.3, they moved EJS and Stylus renderers to external plugins, and there are other plugins, but unless you are willing to make your own theme from scratch, you are stuck with these technologies.
I chose the Icarus theme, responsive, clean and nice, supporting Disqus and Google Analytics out of the box.
I made some minor changes, mostly on the CSS side, to personalize it a bit.
Once Hexo-cli is installed, if we do an hexo
command in the project freshly cloned, it will complain, asking to install Hexo locally. Instead of that, you just have to do npm install
to deploy Hexo locally and all needed plugins.
RxJS has several distributions:
As this is a bit confusing, I made a table out of the official doc, including all mistakes found in this manually generated doc, some of which I point out here, to help tracking them down.
Numbers are size in KB of .min.js as of 4.0.6 around October 2015. To be used only as comparison between versions.
The file names with + are not in the documentation, I found them after installing RxJS. I guess they come from v.4, and that the doc is still about v.3.
File name | Id | Size (KiB) |
---|---|---|
Complete library | ||
rx.all.js | A | 139 |
Lite libraries | ||
rx.lite.js | L | 81 |
rx.lite.aggregates.js | LAg | 17 |
rx.lite.async.js | LAs | 4 |
rx.lite.backpressure.js + | 4 | |
rx.lite.coincidence.js | LCo | 7 |
rx.lite.experimental.js | LXp | 8 |
rx.lite.extras.js | LEx | 10 |
rx.lite.joinpatterns.js | LJp | 5 |
rx.lite.testing.js | LTe | 7 |
rx.lite.time.js | LTi | 9 |
rx.lite.virtualtime.js | LVt | 4 |
Main library | ||
rx.js | R | 72 |
rx.aggregates.js | RAg | 16 |
rx.async.js | RAs (needs RBi) | 7 |
rx.backpressure.js + | 8 | |
rx.binding.js | RBi | 7 |
rx.coincidence.js | RCo | 7 |
rx.experimental.js | RXp | 8 |
rx.joinpatterns.js | RJp | 5 |
rx.sorting.js + | 3 | |
rx.testing.js | RTe (needs RVt) | 7 |
rx.time.js | RTi | 16 |
rx.virtualtime.js | RVt | 4 |
Core library | ||
rx.core.js | C | 15 |
rx.core.binding.js | CBi | 10 |
rx.core.testing.js | CTe | 14 |
Method name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
amb | A | LEx | R | |
case | A | LXp | RXp | |
catch | A | L | R | |
concat | A | L | R | |
create | A | L | R | C |
defer | A | L | R | |
empty | A | L | R | |
for | A | LXp | RXp | |
forkJoin | A | LXp | RXp | |
from | A | L | R | |
fromArray | A | L | R | |
fromCallback | A | L | RAs | |
fromEvent | A | L | RAs | |
fromEventPattern | A | L | RAs | |
fromNodeCallback | A | L | RAs | |
fromPromise | A | L | RAs (4) | |
generate | A | LEx | R | |
generateWithAbsoluteTime | A | LTi | RTi | |
generateWithRelativeTime | A | LTi | RTi | |
if | A | LXp | RXp | |
interval | A | L | RTi | |
just | A | L | R | |
merge | A | L | R | |
mergeDelayError | A | L | R | |
never | A | L | R | |
of | A | L | R | |
ofArrayChanges | A | |||
ofWithScheduler | A | L | R | |
onErrorResumeNext | A | LEx | R | |
pairs | A | L | R | |
range | A | L | R | |
repeat | A | L | R | |
return | A | L | R | |
spawn | A | LAs | RAs | |
start | A | LAs | RAs | |
startAsync | A | LAs | RAs | |
throw | A | L | R | |
timer | A | L | RTi | |
toAsync | A | LAs | RAs | |
toPromise | RAs | |||
using | A | LEx | ||
when | A | LJp | RJp | |
while | A | LXp | RXp | |
wrap | A | RAs | ||
zip | A | L | R |
(! marks functions also on object)
Method name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
aggregate | A | LAg | RAg | |
all | A | LAg | RAg | |
amb ! | A | LEx | R | |
and | A | LJp | RJp | |
any | A | LAg | RAg | |
asObservable | A | L | R | |
average | A | LAg | RAg | |
buffer | A | LCo | RCo | |
bufferWithCount | A | LEx | R | |
bufferWithTime | A | LTi | RTi | |
bufferWithTimeOrCount | A | LTi | RTi | |
catch ! | A | L | R | |
combineLatest | A | L | R | |
concat ! | A | L | R | |
concatAll | A | R | ||
concatMap | A | L | R | |
connect | A | L | RBi | CBi |
controlled | A | |||
count | A | LAg | RAg | |
debounce | A | L | RTi | |
defaultIfEmpty | A | L | R | |
delay | A | L | RTi | |
delaySubscription | A | LTi | RTi | |
dematerialize | A | L | ||
distinct | A | LEx | R | |
distinctUntilChanged | A | L | R | |
do | A | L | R | |
doOnNext | A | L | R | |
doOnError | A | L | R | |
doOnCompleted | A | L | R | |
doWhile | A | LXp | RXp | |
elementAt | A | LAg | RAg | |
every | A | LAg | RAg | |
expand | A | LXp | RXp | |
extend | A | LXp | RXp | |
filter | A | L | R | |
finally / ensure | A | L | R | |
find | A | LAg | RAg | |
findIndex | A | LAg | RAg | |
first | A | LAg | RAg | |
flatMap | A | L | R | |
flatMapFirst | A | LXp | RXp | |
flatMapLatest | A | L | R | |
flatMapObserver | A | R | ||
flatMapWithMaxConcurrent | A | LXp | RXp | |
forkJoin ! | A | LXp | RXp | |
groupBy | A | LCo | RCo | |
groupByUntil | A | LCo | RCo | |
groupJoin | A | LCo | RCo | |
ignoreElements | A | L | R | |
includes | A | LAg (5) | RAg (3) | |
indexOf | LAg | RAg | ||
isEmpty | A | LAg | RAg | |
join | A | LCo | RCo | |
last | A | LAg | RAg | |
lastIndexOf | A | LAg | RAg | |
let | A | LXp | RXp | |
manySelect | A | LXp | RXp | |
map | A | L | R | |
max | A | LAg | RAg | |
maxBy | A | LAg | RAg | |
merge ! | A | L | R | |
mergeAll | A | L | R | |
min | A | LAg | RAg | |
minBy | A | LAg | RAg | |
multicast | A | L | ||
observeOn | A | LEx | R | |
onErrorResumeNext | A | LEx | R | |
pairwise | A | LCo | RCo | |
partition | A | LCo | RCo | |
pausable | A | (L) | ||
pausableBuffered | A | |||
pluck | A | |||
publish | A | L | RBi | CBi |
publishLast | A | L | RBi | CBi |
publishValue | A | L | RBi | CBi |
refCount | A | L | RBi | CBi |
reduce | A | LAg | RAg | |
repeat | A | L | R | |
replay | A | L | RBi | CBi |
retry | A | L | R | |
retryWhen | A | L | R | |
sample | A | L & LTi | RTi | |
scan | A | L | R | |
select | A | L | R | |
selectConcat | A | L | R | |
selectMany | A | L | R | |
selectManyObserver | A | |||
sequenceEqual | A | LAg | RAg | |
selectSwitch | L | R | ||
selectSwitchFirst | LXp | RXp | ||
selectWithMaxConcurrent | LXp | RXp | ||
share | A (1) | RBi | CBi | |
shareLast | RBi | CBi | ||
shareReplay | A (1) | RBi | CBi | |
shareValue | A (1) | RBi | CBi | |
single | A | LAg | R & RAg | |
singleInstance | A | L | RBi | CBi |
skip | A | L | R | |
skipLast | A | L | R | |
skipLastWithTime | A | LTi | RTi | |
skipUntil | A | L | R | |
skipUntilWithTime | A | |||
skipWhile | A | L | R | |
slice | A | LAg | RAg | |
some | A | LAg | RAg | |
startWith | A | L | R | |
subscribe / forEach | A | L | R | |
subscribeOn | A | LEx | R (2) | |
subscribeOnNext | L | R | ||
subscribeOnError | L | R | ||
subscribeOnCompleted | L | R | ||
sum | A | LAg | RAg | |
switch / switchLatest | A | L | R | |
switchFirst | A | |||
take | A | L | R | |
takeLast | A | L | R | |
takeLastBuffer | A | LEx | ||
takeLastBufferWithTime | A | LTi | RTi | |
takeLastWithTime | A | LTi | RTi | |
takeUntil | A | L | R | |
takeUntilWithTime | A | |||
takeWhile | A | L | R | |
tap | A | L | ||
tapOnNext | A | L | ||
tapOnError | A | L | ||
tapOnCompleted | A | L | ||
thenDo | LJp | RJp | ||
throttle | A | L | RTi | |
timeInterval | A | LTi | RTi | |
timeout | A | L & LTi | RTi | |
timeoutWithSelector | LTi | |||
timestamp | A | L & LTi | RTi | |
toArray | A | L | R | |
toMap | LAg | RAg | ||
toPromise ? | L | |||
toSet | LAg | RAg | ||
transduce | L | R | ||
where | A | L | R | |
window | A | LCo | RCo | |
windowWithCount | A | LEx | R | |
windowWithTime | A | LTi | RTi | |
windowWithTimeOrCount | A | LTi | RTi | |
withLatestFrom | A | L | R | |
zip ! | A | L | R | |
zipIterable | A | L | R |
Class name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
Rx.Observer | A | L | R | C |
Rx.Observable | R | C | ||
Rx.Notification | A | L | R | CTe |
Class name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
Rx.AsyncSubject | A | L | R | CBi |
Rx.BehaviorSubject | L | RBi | CBi | |
Rx.ReplaySubject | L | RBi | CBi | |
Rx.Subject | A | L | R | CBi |
Class name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
Rx.Scheduler | A | L | R | C |
Rx.TestScheduler | LTe | RTe | CTe | |
Rx.VirtualTimeScheduler | LVt | RVt | CTe | |
Rx.HistoricalScheduler | LVt | RVt |
Class name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
Rx.CompositeDisposable | A | L | R | C |
Rx.Disposable | A | L | R | C |
Rx.RefCountDisposable | A | L | R | |
Rx.SerialDisposable | A | L | R | C |
Rx.SingleAssignmentDisposable | A | L | R | C |
Class name | In All | Library id for Lite | Library id for Main | Library id for Core |
---|---|---|---|---|
Rx.ReactiveTest | LTe | RTe | CTe | |
Rx.Recorded | LTe | RTe | CTe | |
Rx.Subscription | LTe | RTe | CTe | |
Rx.TestScheduler (see Schedulers above) |
(XYz): not in the lists, but found in the description of the operator.
(1): Out of order in the list at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/libraries/main/rx.complete.md
Also some methods / classes are not in All?
(2): Out of order in the list at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/libraries/main/rx.md
(3): Out of order in the list at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/libraries/main/rx.aggregates.md
(4): Not in https://github.com/Reactive-Extensions/RxJS/blob/master/doc/libraries/main/rx.async.md
(5): Out of order in the list at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/libraries/lite/rx.lite.aggregates.md
FRP is a way to deal with events and asynchronous data in a functional style.
Basically, it is an implementation of the Observer design pattern, coupled with the Iterator one, dealing with streams of data coming on a timeline with a fluid API.
We saw, in the article about functional programming, powerful ways to process iterable data, by composing functions, with lazy evaluation able to deal with infinite data, etc.
FRP does the same, but applied to streams of events, which introduces a new dimension: time.
Events can be seen as some data coming at a given point in time.
Data might come from user interaction: a keyboard key is pressed or released, the mouse moves, is pressed, dragged, released, etc.
Data can also come from a server, after an asynchronous request. File system requests (asynchronous reading of file in Node.js, for example) are similar.
It can also be a timer, delivering a timeout once or regularly.
It can even come from static data (eg. content of an array): in this case, time is “now”.
In his seminal article The introduction to Reactive Programming you’ve been missing, André Staltz wrote the ‘mantra’: “Everything is a stream”.
It looks like a golden hammer seeing the world as nails to hit… I prefer the less slogan-like “Everything can be seen as a stream”, or perhaps even more accurately, “Everything can be put in a stream”.
After all, a stream is just a sequential collection (succession) of items disposed along a time axis, ie. with a timestamp.
It can be applied in the real world, from trains coming to a station to persons lining up at a desk. Even the only time one person went to a concert can be seen as a stream… with only one item.
So streams are a way to model “the world” from the point of view of moments in time, ordered along the time axis.
Purists of the FRP concept, those having coined the term, claim that the implementations I talk about here are not really FRP.
Let’s agree on that and say that what we call FRP here is just a bastardized, simplified, with pragmatic compromises, that happens to use the same term, because it is just convenient…
FRP has been implemented in various languages, particularly in functional languages like Haskell where it is a good fit to their paradigms. But it can be found also in Java, Scala, C#, etc.
It is quite popular in JavaScript, rather functional in nature, where it solves a number of hard problems: avoiding callback hell (chained asynchronous calls), easing usage of promises and timeouts, handling successions of user input, etc.
Thus we saw lot of FRP libraries to be created. RxJS is among the firsts. Bacon was created in reaction to the complexity of the former, Kefir was born because Bacon is slow and memory hungry, More.js is yet another library, etc.
The RxJS book lists a number of them in its Similar libraries page.
The principles explained here can be applied to different languages and libraries, but to avoid being too abstract, I will give examples in JS, using a specific library.
I briefly explored some of the JS libraries, but finally settled on RxJS: it is one of the most complete implementation, it is backed by a corporation instead of being an experiment from an individual, it has good docs and good tutorials.
Another advantage: its API has been defined by the ReactiveX project, and it has been implemented in lot of languages: Java, C#, C++, Ruby, Python, Swift, Rust, and many JVM languages (often via RxJava). Plus some independently developed implementations, like Rx.PHP.
So once you know the API for a language, using it in another language is rather trivial, needing mostly minor adaptations.
One downside of RxJS is its size: it is a large API, so there is lot to learn, and the library is quite big, which can be annoying if you want a lean application (eg. for using on mobile devices).
That said, a large part of the API can be ignored at first, a beginner needs to know only a few functions (called operators in RxJS).
Moreover, the API has been split in various packages: the Core libraries contains only the vital minimum for Rx. The Lite libraries and the Main libraries have a base file with the objects and the essential operators, and other files split by theme: aggregates, async, time, etc. The difference between Main and Lite resides in the way the operators are grouped.
The point is that a project can include only a base file, and add additional ones if a specific operator is needed. This can reduce the amount of code to include in a page.
Or, for experimentations and convenience, just include the rx.all.js
file and be done.
RxJS calls stream “sequences”, and since the point is to observe them and to react on what they content, they call them “observable sequences”. Older documents called them just “observables”, a bit ambiguous, so they adopted the longer term. I might still use the “stream” term too.
You can create an Observable from scratch, but in most case, you just use one of the adapters wrapping common JavaScript entities in an observable sequence.
You can create a new instance of the Rx.Observable
object.
By subscribing to a stream, you create an Observer, which is, most of the time, a Disposable, which can clean up the observed resource automatically or on demand.
Lodash is a JavaScript utility library enabling a functional programming coding style.
Lodash has general purpose functions, for type checking, string utilities, even functions to manipulate functions.
More importantly, it has functions to manipulate collections. Collections is a general term covering arrays, objects (seen as maps) and even strings (seen as arrays of characters).
This allows to replace most for
loops with powerful and succinct function calls, showing clearly the intent of the operation without having to analyze complex code.
In the days of JavaScript before EcmaScript 5 (which added methods like map or filter), JS was mostly used as an imperative language.
The Underscore library (created by Jeremy Ashkenas) changed that by introducing (in 2009) functions like map, filter or reduce to ease processing of arrays. Similarly to jQuery, which adds a global variable named $
to be reachable from everywhere, Underscore adds a global variable named… _
to provide its functions.
An Underscore contributer, John-David Dalton, tired of some limitations and inconsistencies of the library, forked it (in 2012) and named the project Lo-Dash (pun on “low dash”, ie. an underscore). Later, it just took the name Lodash. The library kept the _
symbol, as it is intended to be a drop-in replacement of Underscore: replace one with the other, no other code change, and benefit from higher speed, more functions, more consistency and better documentation.
Today, Lodash diverges from the Underscore API but maintains a compatibility build.
Out of the box, Lodash is bigger than Underscore. Unless you start adding extensions to the latter to get functionalities available in Lodash… And Lodash has a tool to create a build having only the functions you want. And if you use require
on your functions, it will build automatically with only them.
As said, EcmaScript 5 brought some functional features like map or filter on arrays. Underscore uses them if available, hoping their implementation will be someday faster than a manual implementation. Lodash generally prefers its hand-tuned implementations, much faster than current browser implementations. JD Dalton is performance-obsessed (he co-created a benchmark library and the JSPerf site) so he ensures the performance hit of using his library instead of hand-crafted for loops is very minor.
Personally, I started to use Underscore, but I found I needed to mutate an array, to avoid loosing AngularJS bindings. Underscore doesn’t allow that, so I looked into its extensions. But the one allowing maybe to do that was poorly documented. I finally used Lodash and never looked back.
I once made an issue (#2052) on Underscore to point out that some information was missing at one place and had to be found elsewhere. The issue was closed without change.
In contrast, Lodash’s doc repeats all the information needed to use a function in each entry. It might seem tiresome, but the user skips over these repetitions… unless she really need the info! In this case, it is here. Great when jumping to a definition to refresh the memory… or to discover it.
Moreover, I made an issue to point out some little confusion. JD Dalton quickly asked for a pull request, and merged it verbatim within hours.
Note: the article is written for the v.3 of Lodash (more precisely, for v.3.10.1, the last of the v.3 series). Since then, JDD released v.4 of his library, with breaking changes, and lot of new functions. There is also the Lodash/fp version, with different parameter order, easying their currying for better composition.
For now, I remain at v.3, of smaller scope. I might write a new article for v.4.
As said, Lodash has functions acting specifically on arrays, objects (aka. associative arrays, aka. maps) and strings (and more).
But it also has functions that treat these three types as one: collection. They are taken as “bag of items in variable number, ordered or not”.
These functions always take a collection as input, and generally a function, called iteratee, or predicate if it returns a boolean, that processes each item of the collection.
There are functions to filter out items (or to keep only some of them), to map them to something else, to reduce them to a single value, to find an item, etc.
With them, you will never need to write a for loop ever again…
I wrote an article explaining the base principles of functional programming (FP) behind these functions. I invite you to read it before continuing here: although the article is language agnostic, it fully apply to Lodash and I cleverly used Lodash function names there… Actually, most of them are relatively standard in the FP world and can be found in various libraries across various languages.
So here, I will concentrate on the specificities of Lodash, fully adapted to the needs of JavaScript.
As said, lot of Lodash functions take a collection as first parameter, and an iteratee as second one.
Example:
var r = _.filter(list, function (v) { return v.name === someName; });
You will find yourself to write frequently such simple predicate.
So Lodash made a shortcut for this pattern:
var r = _.filter(list, 'name', someName);
Even if you use ES6 and its arrow notation, it is still more concise…
You can even omit the value:
var r = _.filter(list, 'name');
In this case, Lodash will return the value of this property for each object, used here as a truthy or falsy value: here, it will keep out all objects without a name property (undefined), or with a null or empty value.
If you replace filter
with map
, it returns all the name values of the list.
If you replace the property name with an object, Lodash will return true
if the current item includes (deeply) all the properties of the given object with their values.
For example:
var p = _.find(persons, { name: Bond', surname: 'James' });
will return the first person with these name and surname.
You will find these shortcuts in most Lodash functions, if applicable. The Lodash documentation re-explain them on each function supporting them.
Side note: the first form, _.filter(list, 'name', someName);
, has been dropped in Lodash 4. So, to reduce the work for a possible update, I advise to systematically use the _.filter(list, { name: someName });
form instead.
Most iteratees / predicates also share the same set of parameters: they are generally called with the current item, its index if the collection is an array or a string, or its key if that’s a map (an object), and the collection itself.
When a function has an iteratee (or other callback function), it also accepts an optional thisArg
as last parameter. This allows to bind an object to the callback, to access its properties or methods. I haven’t used it yet… And it has been dropped in v.4!
Note that Lodash is robust against undefined
or null
input: it won’t throw exceptions, but will do some kind of no-op, depending on function.
In the article mentioned above (about functional programming), I talk about some common functions:
forEach (each), forEachRight (eachRight), reduce (foldl, inject), reduceRight (foldr), range, filter (select), reject, find (detect), findLast, findIndex, findLastIndex, map (collect), take, takeWhile, takeRight, takeRightWhile, first (head), last, initial, rest (tail), drop, dropWhile, dropRight, dropRightWhile, every (all), some (any), zip, unzip, partial, ary, rearg, negate.
The names between parentheses are aliases (some of them removed from v.4 – avoid using them!).
Except for consistency, I won’t present them again here.
I won’t present all the functions of Lodash, check the official documentation to have an up-to-date list and detailed presentations.
They are numerous. I advice to quickly scan the list, to get an idea of what they do, what are the available tools, then later to check in depth the functions you need.
Let say you want to filter an array, take only the last 10 elements and map them to something else.
You can do that in three steps:
1 | var numbers = [ 10, 55, 6, 98, ... ]; |
This shows clearly each step, which can be nice for debugging, but it is a bit verbose and it creates variables “polluting” the current scope.
Of course, you can nest the calls:
1 | var r = _.map(_.take(_.filter(numbers, function (n) { return n <=10; }, 10), function (n) { return n * 10; }); |
No more intermediary variables, but it is a bit less readable, and the order of calls isn’t obvious.
Lodash allows to chain calls, leading to more readable code, and even to more efficient processing of data.
1 | var r = _.chain(numbers) |
Notice the call to value()
.
It is necessary because this chain is open-ended: you can add other functions after it. Eg. you can end it after the take()
, and have two different values by ending the chain with two different map
s.
value()
tells to end the chain and to evaluate it.
This shows that this chaining is lazy: no processing is done until it is requested by value()
. Moreover, this chaining is optimized, Lodash practices function fusion with shortcut: it doesn’t create an intermediary result on each function call, but rather creates a compound function processing the whole input at once. And, of course, it will not map the data beyond the 10 selected by take()
.
There is a slightly shorter variant:
1 | var r = _(numbers).filter(...)...value(); |
They are basically the same, with a little difference: value()
is not necessary if the final result is a primitive (string, number, …). And it is slightly shorter, although less explicit.
We saw that FP privileges usage of immutable data, returning a new structure instead of changing the given one.
But Lodash is pragmatic, and provides functions that can mutate collections in-place: it might be more memory efficient, and it is useful when you must keep the reference to the data. It is named referential integrity.
For example, if you bind a collection in AngularJS, you have to change it in-place; if you assign a new collection, the binding is broken.
We will distinguish below functions mutating data from those returning a new collection.
Reminder: collection is a generic term. Basically, it covers JS iterable structures: arrays, objects (seen as map, sometime called associative arrays) and strings (seen as a list of characters).
The following functions work for these kinds of collections. Later, we will present functions more specific to each type.
_.includes(collection, target, [fromIndex=0])
(aliases: include
, contains
)
Checks if target
is in collection
. If fromIndex is negative, it’s used as the offset from the end of collection.
In an array, searches the target for each entry. In an object, searches the target in the values. In a string, checks if target is in the string.
_.findWhere(collection, source)
Performs a deep comparison between each element in collection
and the source
object, returning the first element that has equivalent property values (partial matching).
_.where(collection, source)
Performs a deep comparison between each element in collection
and the source
object, returning an array of all elements that has equivalent property values (partial matching).
_.pluck(collection, path)
Gets the property value of path
from all elements in collection
.
In general, collection
is an array of objects, and you want to get all the ids, names or other specific properties of these objects in a new array.
We already saw lot of functions that can be applied on arrays (among other iterable structures). Let’s see some more specific.
_.fill(array, value, [start=0], [end=array.length])
Fills the given array with the given value in the given range (defaulting to full array).
_.pull(array, [values])
Removes all provided values (as list of arguments) from the array.
_.pullAt(array, [indexes])
Removes elements from the array at the given indexes (as list of arguments or array of indexes).
Returns an array of removed values (null value if index is out of range).
_.remove(array, [predicate=_.identity], [thisArg])
Removes all elements from array that predicate returns truthy for, and returns an array of removed values.
Supports property shortcuts.
_.prototype.reverse()
Reverses the wrapped array and returns the wrapped instance (for chaining).
_.without()
Same as _.pull
but doesn’t mutate the array.
_.at()
Same as _.pullAt
but doesn’t mutate the array. Work on any collection.
_.filter()
Same as _.remove
but doesn’t mutate the array. Work on any collection.
_.flatten(array, [isDeep])
Some Lodash functions return an array of arrays. flatten
put all values in the same array, linearly.
_.uniq(array, [isSorted], [iteratee], [thisArg])
(alias: unique)
Eliminates duplicate entries from the given array, returning a new array.
_.union([array])
Creates an array of unique values, in order, from all of the provided arrays.
_.difference(array, [values])
Creates an array of unique values not included in the other provided arrays.
_.intersection([arrays])
Creates an array of unique values that are included in all of the provided arrays.
_.xor([arrays])
Creates an array of unique values that is the symmetric difference of the provided arrays.
Objects in JS are dynamic: sometime, an information is optional or missing.
For example, you might want to access order.customer.addresses.shipping.zipCode
.
The shipping address can be optional, eg. if it is the same than the customer’s address.
Plus we can have missing (undefined
) information at any level.
We can put the access in a try / catch block. But that’s ugly.
Or we can check each level up to the one we want:
if (order !== undefined && order.customer !== undefined && order.customer.addresses !== undefined && ...)
Tiresome and prone to errors. Idem for the shortcut version order && order.customer && order.customer.addresses ...
.
Lodash offers functions to access an arbitrary depth of nested objects without throwing errors.
_.get(object, path, [defaultValue])
Accesses the property at the given path.
Example: var zip = _.get(order, "customer.addresses.shipping.zipCode");
gives the value if available, or undefined
(or the default value, if provided) if one level is missing.
_.set(object, path, value)
Is similar, except it sets the value at the given path, creating intermediary objects if needed.
It mutates the object.
_.has(object, path)
Checks if path is a direct property.
1 | var oo = { x: 'foo', y: { z: 'bar', u: undefined } }; |
Note that path
can also be an array: [ 'customer', 'addresses', ... ]
It can include indexes of arrays: "customer.addresses[1].zipCode"
Elegant and flexible ways to merge objects and set default values to missing properties.
These functions mutate the destination object, and return the result.
It is not uncommon to give an empty object {}
as destination, if we want to merge two objects but to leave them intact (or if one of them can be undefined).
_.assign(object, [sources], [customizer], [thisArg])
(alias: extend
)
Assign the (own) properties of source object(s) to the destination object, each additional source property overwriting the previous ones, including source properties set to undefined.
_.defaults(object, [sources])
Assign the (own) properties of source object(s) to the destination object, only if the destination property is undefined
. Once a property is assigned, it won’t change.
_.defaultsDeep(object, [sources])
Is like _.defaults
but it recursively assigns default properties.
_.merge(object, [sources], [customizer], [thisArg])
Overwrites deeply all (own) properties with successive values, except undefined source properties. Kind of deep assign
.
Example:
1 | var dest = { name: 'Foo', age: 25, driver: true, |
A method I use often is pick
: it allows to do a partial clone of an object, where you specify the properties to copy.
_.pick(object, [predicate], [thisArg])
Predicate can be a function, a single key, a list of keys as parameters, or an array of keys.
_.omit(object, [predicate], [thisArg])
Does the reverse of pick
: it clones the object, removing the given properties.
_.keys(object)
Creates an array of the own enumerable property names of object
.
_.values(object)
Creates an array of the own enumerable property values of object
.
_.mapKeys(object, [iteratee=_.identity], [thisArg])
Creates an object with the same values as object
and keys generated by running each own enumerable property of object
through iteratee
.
_.mapValues(object, [iteratee=_.identity], [thisArg])
Creates an object with the same keys as object
and values generated by running each own enumerable property of object
through iteratee
.
_.findKey(object, [predicate=_.identity], [thisArg])
Finds the propoerty identified by the predicate, and returns its key.
_.transform
is similar to _.reduce
, just a bit simpler. Compare:
1 | _.transform([ 'a', 'b', 'c' ], function (acc, value, key) |
In both cases, we get
["0 -> a","1 -> b","2 -> c"]
_.reduce
's default value for the accumulator is the first value of the object, while _.transform
's one is an empty array or object, depending on the type of the input. Actually uses the prototype of the object, so it really creates an object of same type.
_.transform
’ doesn’t need to return the accumulator. Actually, if the return value is false
, the iteration ends there.
Reminder: case is a typographical style: uppercase (A) or lowercase (a).
JavaScript allows to change case of a whole string: s.toUpperCase()
and s.toLowerCase()
.
Language identifiers (and file names, etc.) can have various styles:
Lodash offers a number of functions to convert from any style to one of these.
_.camelCase
: foo--bar-
, __foo_bar
, Foo Bar
=> fooBar
_.capitalize
: foo bar
=> Foo bar
_.kebabCase
: FooBar
,_foo__bar_
, foo Bar
=> foo-bar
_.snakeCase
: foo bar
, fooBar
, Foo-Bar
=> foo_bar
_.startCase
: foo bar
, --Foo-bar
, _foo_bar
=> Foo Bar
_.deburr("ça déjà tôt")
=> ca deja tot
_.startsWith("aaron", "aa")
-> true
_.endsWith("file.txt", ".txt")
=> true
_.repeat("=-", 3)
=> =-=-=-
Plus some other functions too numerous to detail: see doc for more.
A template
function allowing string interpolation.
Two escape functions to escape some characters in HTML and regexp. There is also an unescape function.
Three pad functions to fit a length.
Three trim functions to remove characters (spaces by default) from left, right or both sides of a string.
trunc
cutting a string at given length and separator, with a continuation string (ellipsis by default).
words
cutting a sentence in words.
Ie. functions manipulating functions, aka. higher-order functions.
See the FP article on the use cases for the functions.
We saw partial
to pre-fill arguments, ary
to reduce the number of parameters, rearg
to change their order, and negate
to invert the result of a predicate.
_.debounce(func, [wait=0], [options])
_.throttle(func, [wait=0], [options])
These two functions seem similar, but differ in effect (of course!) and usage.
They are used when a function is called frequently at irregular intervals. A typical example is a callback on user input. These functions limit the number of calls effectively done, to limit the pressure on the process.
debounce
prevents calling the function unless it hasn’t be called for some time.
For example, we set a callback on key press in a text field. There, we want to do an Ajax call to search for the text input so far. But we don’t want to do too frequent calls if the user types quickly: we can have a new character before the previous request response arrives.
So we wait for a pause in the typing to issue the request: we prevent calling the function, but if the function hasn’t been called for a given time, then we really call the function with the latest result.
throttle
allows calling the function only once in a given interval.
It is typically used to regulate a great amount of calls, like mouse moves (dragging) or scroll events.
It cuts time in windows of given interval, and it blocks all calls except the last one of the window (or the first one if specified by an option).
So when you have a stream of events to process, it samples these events to reduce the amount of processing.
Lodash offers a number of functions to check the type of a given value:
isUndefined, isNull,
isObject, isArray, isFunction, isNative (function),
isBoolean, isDate, isNumber, isRegexp, isString,
isArguments, isError, isTypedArray, isElement (Dom element),
isNaN, isFinite
Their name is explicit enough to dispense of a short description. See doc for details.
They a particularly useful to ensure an argument has been passed to a function.
_.isEmpty
checks if the given value is empty. Can check for arrays, objects, strings and jQuery-like collections,
_.isEqual
does a deep comparison between two values to determine if they are equivalent. A customizer function allows some fuzzy logic (eg. case-insensitive comparison, etc.).
_.isMatch
does a deep comparison to determine if the given object includes all the properties of the source object. Ie. that’s a partial isEqual
, ignoring properties in given object not in source object. Also accepts a customizer function.
_.clone
and _.cloneDeep
create respectively a shallow and a deep clone of the given value. Accepts a customizer.
As said, Lodash is a very rich library of general purpose functions, particularly in the field of collection manipulations.
It is very efficient, practical, and results in readable code.
I invite you to explore the list of functions, to get familiar with them, to be able to pick up the right tool for the right job…
Happy coding!
]]>Integrated Development Environment oriented toward the Web technologies
I installed Brackets (v. 1.2 on Windows 7 with 8 GB of memory; currently at v.1.4), it went out quite smoothly, just asking for the directory where to install it.
I opened it, it starts very fast.
It has a dark UI but a light editing area. Not fan of dark theme, but I will try and keep it to see if it is effective to have a contrast between the UI and the editing area.
Default syntax coloring theme looks OK so far. It can be changed with theme plugins.
Actually, the UI theme cannot be changed easily, it needs a plugin to be able to modify it.
There is no drag’n’drop of text by default, which is why I rejected this editor on my first try (before 1.0), because I use this operation a lot; but it has been introduced in this version, and the blog post about this release shows how to activate it.
The editor is a bit primitive out of the box. You have to install several extensions to get things done efficiently… That’s OK because this keeps the application streamlined: no need to install CoffeeScript or Less extensions if you code only in JavaScript and use Sass…
There are not much settings either: you have to edit Json files to customize things. A bit less intuitive, but I am OK with that, as that’s the way I customize ScITE, my favorite (general purpose) source code editor. And not having complex dialogs to set every option also keeps the application light.
After a few weeks of using Brackets, I appreciate it a lot: it is much less memory hungry than WebStorm or Eclipse, it is flexible and easy to use.
I recommend it.
Still, I regret some base editor features are missing and must be fixed with extensions. Among them:
Some of these features has been addressed by adding extensions, but I feel these should be native.
I will list here a number of these extensions I found useful.
I give the links to the GitHub projects, but the best way to install these extensions is to go to the Extension Manager in Brackets, to type (paste) the name of the extension and to install it from there. The manager also checks for updated and allows to install them easily.
File Tree Exclude
Brackets Outline List
White Space Sanitizer
Show Whitespace
Select Lines
Display Shortcuts
Go to Matching Bracket
Rename JavaScript Identifier
BracketstoIX
Interactive Linter
SCSS Lint
SASSHints
Emmet
Brackets Snippets
Ternific
QuickDocsJS
FuncDocr
JSHint
JSCS
CSSLint
Brackets SASS
Markdown Preview
Spell-check
Perforce
SVG Preview
Beautify
Git
Functional Programming (FP) is a programming paradigm, like imperative (procedural) programming or object-oriented programming (OOP).
Some people oppose these paradigms, but actually they are not exclusive of each other: we do imperative programming inside OOP’s methods, and we can mix OOP and FP: that’s even a trend in modern languages (Scala, Ceylon, …) and in older ones (JavaScript, Java 8…).
FP exists for years, but it was rather confined to niche languages. It started to infuse in the mainstream coding practices via the above mentioned languages or libraries like Guava (Java) or Underscore / Lodash (JavaScript).
By promoting stateless functions and immutable data, it proved to be very efficient and easy to control in concurrent programming, leading to efficient processing of big data.
It is also easy to test. Its rather declarative style is also easy to read.
And reactive functional programming (FRP) is a powerful paradigm to process asynchronously big streams of data as well as user input.
Given all these qualities, why isn’t more popular?
Very popular languages like C++ or Java are based on OOP, which is easy to grasp: we can relate objects to the real world. So there were a generalization of OOP thinking.
On the other hand, FP comes from lambda calculus, a “formal system in mathematical logic”. Ie. it has strong mathematical roots, thus some of its concepts can be quite abstract, and it shows when most people explain its concepts. Combined with strong typing, based on type theory, like in Haskell, we quickly end with abstract concepts with specialized jargon. Plus Haskell, one of the most popular FP languages, has a syntax not intuitive for the uninitiated.
Fortunately, as said, some people extracted some core ideas from FP, and exposed them in easier to understand languages and libraries (see above). This helped in spreading these practices in mainstream programming.
This article is rather long, but only scratches the surface. One can write a whole book on the topic.
So it is only a brief overview. Masters and purists of FP will frown upon many omissions and perhaps inaccuracies. I am not a master and certainly not a purist, and don’t plan to be one of the latter. I probably won’t complete the article with finer points of FP (monads, etc.) but I will try and fix any inaccuracies that I spot or that are reported.
It is pragmatic because I use several FP techniques, as popularized by some libraries or languages, but I don’t use the full spectrum of FP possibilities.
So, what functional programming is about?
As the name implies, like OOP is all about objects, FP is all about functions.
These are first-class citizens: they can be stored in variables and collections, they can be passed as arguments to functions which can return a function. You get the idea…
Note: functions manipulating functions are called higher-order functions. I will spray a bit of FP jargon here, for your information; for something more complete, see Functional programming jargon, an accessible, work-in-progress glossary.
A central idea behind FP is to use pure functions: they must not change the world outside of them. A pure function will always return the same output for a given input. It is called idempotency and referential transparency. The later means that if a pure function is called with a constant, it can be replaced with the value it returns.
Of course, a real program has to alter its environment: to take some data, to output some results, perhaps to react to user input.
Pure FP isolates these side-effects in a special concept: monads. Which I won’t attempt to explain here!
Choosing the pragmatic approach, I tolerate functions with side-effects, but avoid them as much as possible, documenting these side-effects and, ideally, isolating them in specific modules. Which is a good practice for unit tests…
As a consequence, FP favors immutable data. Functions avoid to mutate the data given as input (that would introduce side-effects), but return new data instead.
It might seem inefficient but there are techniques to optimize this practice. Among them, lazy evaluation defers processing until the result is really needed. This is particularly effective when chaining several operations, which is a very common practice in FP, as we will see.
FP doesn’t like loops (those made with for
, while
, repeat
, etc. keywords). These are stateful constructs, with one or more loop variables. FP often uses recursion instead, thus purely relying on functions. But to be effective, the language must support tail-call recursion to avoid bloating the call stack. This means that a function returning the result of calling itself can avoid to push a call reference (to return to the calling point) and can instead jump directly into the new call. Yes, that’s a kind of goto…
Recursion can be hard to get right, and some languages like Java or JavaScript (up to ES6) don’t support tail-call recursion, so it is rarely used outside of specialized usages like iterating on tree or graph nodes.
Fortunately FP offers a wide range of functions to process (or create) collections, which are the most common use case for loops. We will extend on this topic later.
Another important concept in FP is closures. When a function returns another function, the inner one (the closure) can use outer variables in its scope. In general, usage is limited to the immediate scope, ie. variables and parameters of the enclosing function. The closure keeps a reference on the content of the used variables at the time the function is returned. We say the inner function closes over these values, hence the closure name.
It is a double edged sword, as these captured values might be mutated (if possible / allowed for the value), introducing state in the function, something that FP avoids…
Let’s make an example for this rather abstract concept. I use JavaScript (ES6) here. I make a generic function that can return specialized variants:
1 | const adder = (step) => |
Note that a function can have state that doesn’t affect its idempotency. A common example in FP is memoization. A function with a long computation time (Fibonacci, is prime, etc.) can keep previous results. Thus, if asked again with a previously computed argument, it can just return the previous result. It is particularly effective with functions whose computing of a value is based on results for lower values. The given recursive functions above are good examples.
Since I started to use the FP style of coding in a JavaScript project, using the Lodash library, I no longer use the for loop. Or other forms of loops, which are rarely used anyway.
If you look at the loops in your code, you will see some very common patterns of usage.
for
loops can be used to iterate an arbitrary number of times to perform an action. Eg. creating objects to put in a collection (or the Dom, etc.).
But most of the time, they are used to iterate on a collection: entries of an array or a list, characters of a string, values of a map or set, etc. “Collection” is used here in the widest sense, data structure that can hold several items in variable number. Tree or graphs are more often explored with recursive functions, but the language / library can offer iterators flattening them with various strategies.
So why do we iterate on these collections?
Sometime, it is to find an item. It can be also to compute a value (sum, average…). Or to process each item, perhaps to transform them into something else. We might want to remove some items, or to process only some items, based on some criteria. Or to process (or extract / remove) only the n first or last items of the list, n being arbitrary on depending on a condition. Other common usage is sorting, removing duplicates, etc.
FP got us covered for all these cases, and more.
The philosophy is similar to the Unix one, which offers lot of small utilities focused on a simple task. They have all a similar interface: accept data on standard input, spit out processed data on standard output, and they are chained via pipes.
FP offers lot of small functions focused on a simple task. They generally take a collection and a function to process each item.
These small functions can be used alone, but they shine when they are chained together. For example, you can take a list, filter out some elements, keep only the unique items and transform the remaining ones, in one operation.
That leads to a rather declarative style of programming (a bit like SQL) which is generally more readable than a bunch of conditions, intermediary variables, sub-loops, etc. that re-implement the same old algorithms…
Contrived example: given an array of numbers, eliminate those below 10, keep only the first five, and sum them.
In a C-like language, that would be something like:
1 | var i, v, r, c; // assume they start at zero |
In functional style:
1 | var r = chainOn(values) |
A nearly literal transcription of the requirement! Much more readable: the intent appears immediately.
As said, each function returns a new collection, but a good library does lazy evaluation, deferring process until the end of the chain, building a complex function out of the bricks, generating only one final collection (or none in this example).
Let’s take a look at the most common functions (also called operations or even operators). For clarity, I give a name to each, but these names can vary, depending on language / library. For example, reduce
can be named reduceLeft
, fold
, foldLeft
, etc. But their base idea is the same everywhere.
Note that the order of parameters can change, depending on language / library.
Nearly all these operations take a collection as input, and most of them also take a function called on each iteration on the collection, thus named iteratee. Or predicate if it returns a boolean.
The parameters given to iteratees vary, but always include the current item. They can also include the index in the array or the key in the map, sometime a reference to the collection itself, etc.
Note: I will use either the term list or array to talk about ordered collections. In some languages they are the same, in others not, but the differences are not relevant for that article.
forEach
(or for
, forAll
, each
, etc.) is just a functional version of our old for
loop. It has a forEachRight
(or eachRight
, etc.) variant iterating on reverse order.
The advantage of this function is that it can be used at the end of a filter / transform / etc. chain.
It is (should be) used only to produce side-effects for each item: create elements in the Dom, serialize the item, issue a Rest request, etc.
There is a variant, called tap
or do
, that can be put in the middle of the chain: it doesn’t alter the data but can do side-effects at this point of the processing.
reduce
(synomyms above) is a fundamental operation: it can be used to do all the other ones. As such, it should be used only in special cases, as more expressive and terser (simpler) operations should be preferred.
It takes three parameters: the collection, the processing function and an initial value.
The iteratee is called with a parameter (named accumulator) holding first the initial value, then the value returned in the previous iteration.
The second parameter is the item itself.
The function processes the item and alters the accumulator, then returns the latter.
The initial value / accumulator can be a number, a collection (often empty), etc.
For a number, the function can sum up the values in the accumulator, or store the min or max, etc. It can push or add values in a collection accumulator. Etc.
The reduce
function returns the final value of the accumulator. It is called thusly because it reduces (or folds) the items of a collection to a single value.
range
allows to create a list of numbers (or characters) between two bounds. This list can be used as an entry point for other functions, thus effectively replacing the numerical for
loop.
find
searches the collection and returns the found item, if any (behavior if not found varies with language). The iteratee is a predicate, inspecting the item and telling if that’s the right one. If so, it stops there and returns the found item.
A variant for ordered collections, findIndex
, returns the position of the item instead.
On ordered collections, we can use findLast
or findRight
, iterating on the items in reverse order. These suffixes can be found on other operations for lists or similar.
filter
returns a reduced version of the collection, keeping the items for which the predicate returns true. It has a variant, reject
, dropping items whose predicate is true.
You might wonder why we have two functions when you just have to invert the condition in the predicate. Actually, we have a number of predefined predicates like isNumber
or isEmpty
. So if you want to remove empty arrays (or strings) out of a collection, you can just use reject(coll, isEmpty)
.
You can also do filter(coll, negate(isEmpty))
, but the intent is less clear. Still it is a good first example of a function altering the behavior of another function. We will see more later.
Variants on ordered collections allow to keep the first (or last) items, based on a predicate: takeUntil
keeps items until the predicate is true, takeWhile
keeps them while the predicate is true. take
keeps the number of items given as argument.
The Last
(or Right
) suffix is applicable. And you can replace take
with drop
to keep the other part of the list.
There are specialized variants for one item, a common operation: first
, last
, rest
, initial
keep or drop the first or last item of a list.
zip
takes a number of arrays / lists, and returns an array of arrays, where the first one groups the first items of the arrays, the second one holds the second items, etc.
For example, you can make an array of [ x, y ]
coordinates from an array of x
s and one of y
s. Or you can associate items with values of a range.
There is an unzip
operator doing the reverse operation.
map
transforms the items of a collection into something else collected in a new collection of same kind. It can be used to transform objects of some type to another type, to extract a value from objects, etc.
every
or all
returns true if the iteratee returns true for all the items of the collection.
some
or any
returns true if the iteratee returns true for at least one item.
There are lot of other functions, to sort or shuffle an array, to randomly sample some values, to eliminate duplicates, etc.
Explore your library, get familiar with its capacities, to know the palette of tools at your disposal; you can dig more on their usage when you need them.
As we saw, there are functions to alter functions, to alter their signature to something expected.
It eases reusability: you have lot of simple functions, from libraries or your own. You can compose (combine) these functions into more powerful functions.
But sometime, the signature of a function of your palette doesn’t fit what is expected in the composition.
You can wrap manually the function to adapt it:
filter(numbers, function (n) { return !isPrime(n); }
But it is verbose and tiresome.
Or you can use a higher-order function automatically transforming (wrapping) the function:
filter(numbers, negate(isPrime));
This negate(isPrime)
way of providing a function is called point-free style (or tacit programming). This is because the parameters (points) are not visible, they are tacit, implicit.
More on this later, but let see other examples first.
Let say we have a toNumber
function taking a base (2, 10, 16…) and a string to convert. If the base is omitted, it default to 10. We need to convert an array of strings.
We can use map
for that. If the numbers are hexadecimal, we can write in JS:
numbers = map(hexaStrings, function (s) { return toNumber(16, s); });
A bit verbose (can be better with arrow notation), not very readable.
But we have a JS method named bind
, or a (simpler) Lodash method named partial
, that can preset the first parameters of a function.
1 | numbers = map(hexaStrings, partial(toNumber, 16)); |
It takes a function with n parameters, and returns a function with less parameters, some of the original parameters being preset.
Such operation is called partial application. Or, if only one parameter remains, currying. The latter has a special name because it is an important process to help in function composition, combining functions to make a complex one.
If the array had decimal numbers in strings, we could use the default value of the base parameter. But the base being the first parameter, we cannot pass it directly to map
. No problem, we have a function able to reorder the parameters of a function:
1 | numbers = map(decimalStrings, rearg(toNumber, [ 1, 0 ])); |
The second parameter (index 1) becomes the first one, while the first one goes to the second place.
partial
is better there, but this contrived example aims to show a semi-realistic use case of rearg
…
But actually, map
calls the function with two parameters: the value and its index! So we convert the strings to bases 0, 1, 2, etc. Not what it was intended!
Fortunately, there is a function for that™… A function that reduces the number of parameters of a function. This number, BTW, is called arity. The function (in Lodash) is called ary
:
1 | numbers = map(decimalStrings, ary(rearg(toNumber, [ 1, 0 ]), 1); |
Now, it is really less readable but the point was to show we can manipulate functions like we process collections.
Somehow, we can often describe a process with a list of functions / operations: filter, map, sort, uniqueItems, map, take, for example. We could put these in an array, and apply these functions successively. Some languages make an heavy usage of this scheme and have a syntax for these chains of functions.
For this kind of chaining, it is easier if all these functions take only one parameter: the result of one function is fed as argument to the next function. That’s why currying is so important.
As said, I only scratched the surface of the topic, yet I introduced lot of concepts that are useful and practical in daily tasks of your favorite language. Although I admit in some languages like pre-8 Java, the syntax (with anonymous classes) can be a bit heavy.
FP techniques proved to be useful enough that some languages (Java 8 again, JavaScript at ES6 / ES2015) evolved to ease their usage. And lot of new ones integrated them from the start.
I hope this article will give you the will to use (more) these techniques, and perhaps to dig more into it.
If you want to explore more functions via the Lodash library, you can continue the reading with my article on Lodash.
Integrated Development Environment oriented toward the Web technologies
https://code.visualstudio.com/
I installed Visual Studio Code (v. 0.10.3 on Windows 7 with 8 GB of memory).
I opened it, it starts reasonably fast.
It has a dark UI, which I don’t like, but I quickly found where to change this to the default light theme. Good point than several themes are bundled by default: no need to hunt for them.
Surprisingly, drag’n’drop of code isn’t enabled.
Apparently, it doesn’t even exist!
http://visualstudio.uservoice.com/forums/293070-visual-studio-code/suggestions/7763958-drag-n-drop-selections
Same for column (rectangular) selections!
https://visualstudio.uservoice.com/forums/293070-visual-studio-code/suggestions/7761618-implement-column-mode-selection-editing
These are features I use extensively in all editors / IDEs I use, I feel cripled if I don’t have them…
End of test… See you later.
]]>There are several use cases for testing if a variable or property is defined. We will examine them before examining the ways to check the state.
Check if a global variable exists.
Of course, in our code, we carefully avoid to create global variables… We use var
each time we declare a variable, and 'use strict';
guards against forgetting it. Of course, we have 'use strict';
everywhere, and an ESLint, JSHint or similar tool configuration to enforce it.
But still, we can have global variables, at least when we load ‘old-school’ (not AMD / CommonJS enabled) libraries: jQuery’s $
and Underscore / Lodash’s _
are famous examples.
In generic code, we might want to check if these variables are defined, and if not, to provide an alternative implementation.
Reminder: in a browser, these global variables are attached to the window
object.
In the general case, we have two scenarii: the variable is not declared at all, or it is declared but no value has been assigned to it yet.
In the first case, if we try to use it, we will have an error (ReferenceError) complaining about a non-existing variable.
In the second case, the variable has the value undefined
.
Check if a local variable exists.
Actually, I will ignore this one: it is the same case than the first one, and a simple look at the code around is better than coding a check! It is here only for exhausting the cases…
Check if a property is defined on an object.
That’s the most common case. It can be used against global objects (eg. to see if a browser object has one of the latest features), against library objects (to handle old versions or optional features), against function parameters (optional properties on a parameter), etc.
Check if a function parameter is defined.
A function expecting a number of parameters can be called with only part of them, or even none.
In this case, the missing parameters (at the end of the list) are declared but have the value undefined
.
JavaScript offers several ways to do this check. What is the “best” one?
As often, the answer is: “It depends”…
A rather universal / safe way is to use the typeof
operator:
1 | if (typeof someIdentifier == 'undefined') |
Advantage: it works in all cases, even when the variable is not declared at all (that’s the only case where we can use an undeclared variable without throwing an error). But honestly, it is verbose / cumbersome… :-) And I dislike using strings in code like that: if you type 'undfined'
instead, it can go unnoticed for a long time…
Note: since typeof
is guaranteed to return a string result, we can use the ==
check. You might prefer to use ===
comparison if that’s the policy of your project (which might be enforced by some tools).
A simpler way to do the check is to compare against undefined
:
1 | if (someIdentifier === undefined) |
Beware: don’t use ==
as automatic type conversions done by JavaScript will bite you!
Some people object that this method can be flawed because JavaScript doesn’t prevent from assigning a value to undefined
, which is not a keyword. It is just a variable attached to the global object (window
in a browser):
undefined = {};
is legal!
Now, it is unlikely to happen, unless somebody made a programming mistake like forgetting an =
in an oddly constructed test: if (undefined = foo)
.
If you include in your code base some code from an inattentive adept of Yoda conditions, you have a bigger problem than you thought… :-)
And EcmaScript 5, implemented in all major modern browsers, now prevent this, making (at least!) undefined
immutable.
Although you can still define a local variable shadowing the global undefined
. But, again, defining a var undefined
in your code is unlikely (or malicious!).
So, unless you target very old browsers or you are very paranoid, it is safe, short and explicit.
if (foo)
or
if (foo.bar)
I mention it because its usage is common. But it is generally a bad way to do the check.
First, the first form fails (with the error “ReferenceError: foo is not defined”) if the variable is not defined at all. Now, the check is generally done for a property or local variable / parameters, which is safer. Note that so called global variables are, on the browser, properties of the window
object, so instead of writing if (foo)
, we can always write if (window.foo)
, and it won’t crash as in the first form.
Second, it is a brittle check: it will fail if foo.bar
is undefined, as expected, but also if it is “falsey”, the JavaScript term for all values evaluated as false because of type coercion. So it also fails if foo.bar
is defined but has the value false
, or is an empty string, or zero, or null
, etc.
Not very trustworthy…
To be honest, this form of test is mostly used when foo.bar
is expected to be a function, to check if we can call it or not. In this case, it is unlikely to be 0 or ‘’ or false…
It is also OK if the variable / property must be an object. It is often used in the idiom var x = foo && foo.bar;
to avoid getting a property on an undefined object.
Several libraries offer facilities to properly do this check.
AngularJS offers several functions for that: isDefined()
, isUndefined()
, but also specialized functions to check if a variable is defined and has the proper type:
Underscore and Lodash have similar functions:
Lodash has some more functions:
jQuery also has checks:
And so on.
]]>https://www.jetbrains.com/webstorm/
For Java coding, I elected Eclipse as my main IDE. I tried NetBeans, and appreciated it, but at the time, it was Java-centric so I went back to Eclipse instead, more polyglot. Now, NetBeans is more versatile, but now I know Eclipse well, with its quirks, warts and all, so I remain there.
When I had to code JavaScript (with AngularJS, how original!) at work, for a new project, I naturally tried to use Eclipse, with an Angular plugin. It wasn’t so bad, but still a bit frustrating, with no renaming of local variables, loosing the capability to drag’n’drop code in CSS or HTML editors (a long-standing bug…), and some other frustrations.
A colleague advised to try WebStorm.
At first, I was a bit reluctant. First, it is a payware, something I usually avoid. Of course, there is chance my company finally pays for the software, if it was a real productivity tool. But then, that’s something that I would use at work, and couldn’t use at home (I have zero software budget…).
Secondly, I tried IntelliJ IDEA some years ago, and wasn’t convinced. Perhaps it was less mature than now, or perhaps I didn’t try hard enough, but I went back to my Eclipse quite quickly.
Well, I installed it (v. 9.0.3 on Windows 7 with 8 GB of memory) and, as usual in a first contact, I went to the settings, to get a first feel of the capabilities of the application. They are quite complete, yet not overwhelming.
I first chose the Eclipse keyboard configuration, allowing me to find back a familiar interface. And I customized some that I also customize on Eclipse (to find back shortcuts I use in my favorite source code editor, SciTE). It went well and fast.
I also customized the color settings. IIRC, it defaults to a dark theme, which I dislike. I chose a clear theme, and customized the colors to my taste. It is quite flexible, too.
Lastly, I changed the JavaScript formatting to follow the rules we use at work. Also easy and flexible, with still some quirks that have corresponding open issues in their backlog.
One surprise: I imported our project, I selected Perforce (the VCS we use at work) as VCS for it (or it even proposed it, perhaps because it found a .p4ignore file), and it went forward without asking any question (except my password), finding settings in the environment variables. The VCS integration is rather well made.
I have collected a little list of pros and cons:
PROS
CONS
I hadn’t time to test how to debug JavaScript within WS, but it also something it can do.
Overall, I am quite impressed by this IDE, which has a deep knowledge of the Web environment, offers good editing facilities and is very pleasant to use.
]]>Note: this is a draft, based on a Google+ message I made years ago.
I am only a beginner with this language, so I recommend to take a look at the Quick Introduction [1] and Tour of the language [2] to have a better, less subjective and more accurate overview.
Here is my take:
[1] http://ceylon-lang.org/documentation/1.0/introduction
[2] http://ceylon-lang.org/documentation/1.0/tour