<commit_before># Point free async utilities

This library designed to encourage composition of a program from Node.js style
asynchronous primitives. It provides generally useful:

- decorators - wrappers that alter async function behavior some way,
- combinators - things that combine several functions into one.


## Installation

```
npm install point-free
```


## Decorators

### retry([options | attempts = 5], func)

### limit([options | limit], func)

### fallback(defaultValue, func)

### logCalls([logger = console.log], func)

On each function call pass its `arguments` to `logger`.


### logExits([logger = console.log], func)

On each function callback call pass its `arguments` to `logger`.


### logErrors([logger = console.error], func)

Pass all function errors to `logger`. They are still passed the normal way too.


## Combinators

### waterfall(funcs...)

Combines several functions to be executed serially with results of each function
passed to next one. Arguments to resulting function before callback are passed to the first step.
Results of last function will be returned as a result of combined action.
Any error will be passed out immediately, stopping chain of execution.

```js
var pf = require('point-free');

var displayFile = pf.waterfall(
    fs.readFile,
    console.log.bind(console),
);

displayFile('filename.txt', callback)
```

When it's not possible to pass everything to first function `waterfall()` could be enclosed and either called immediately...:

```js
function copyFile(from, to, callback) {
    pf.waterfall(
        fs.readFile.bind(null, from),
        fs.writeFile.bind(null, to),
    )(callback);
}
```

... or passed to other combinator or decorator:

```js
pf.serial(
    action1,
    // Combined subtask
    pf.waterfall(
        fetchData,
        actOnIt
    ),
    // ...
)
```


### serial(funcs... | funcs)

### parallel(funcs... | funcs)

### manual(states)

```js
function cachedGet(url) {
    var filename = __dirname + '/cache/' + url.replace(/\//g, '#');

    return pf.manual({
        // always starts from 'start' state
        start: function (next) {
            fs.exists(filename, function (exists) {
                // go to some new state
                if (exists) next.readCache()
                else next.request();
            });
        },
        request: function (next) {
            // use state transition as callback
            request(url, next.writeCache);
        },
        readCache: function (next) {
            // use next.end to leave state machine
            fs.readFile(filename, 'utf-8', next.end);
        },
        writeCache: function (response, body, next) {
            fs.writeFile(filename, body, 'utf-8', function (error) {
                next.end(error, body);
            });
        }
    });
}

cachedGet('http://...')(function (err, body) {
    ...
})
```

### auto(jobs)

### while(test, body)

### doWhile(body, test)


## Primitives

### noop

A nice thing when you want to do something conditionally:

```js
pf.waterfall(
    jobs[id] ? pf.noop : pf.loadJob,
    // ...
)
```


### sleep(timeout)

```js
var delayedHandler = pf.waterfall(pf.sleep(1000), handler);
```


## Collections

### each(seq, func)

### map(seq, func)

### chunk(size, seq, func)

```js
// Insert links into database in chunks of 1000
pf.chunk(1000, links, function (chunk, callback) {
    db.insert('link', chunk).run(callback);
})(callback)
```<commit_msg>Add TODO to README<commit_after># Point free async utilities

This library designed to encourage composition of a program from Node.js style
asynchronous primitives. It provides generally useful:

- decorators - wrappers that alter async function behavior some way,
- combinators - things that combine several functions into one.


## Installation

```
npm install point-free
```


## Decorators

### retry([options | attempts = 5], func)

### limit([options | limit], func)

### fallback(defaultValue, func)

### logCalls([logger = console.log], func)

On each function call pass its `arguments` to `logger`.


### logExits([logger = console.log], func)

On each function callback call pass its `arguments` to `logger`.


### logErrors([logger = console.error], func)

Pass all function errors to `logger`. They are still passed the normal way too.


## Combinators

### waterfall(funcs...)

Combines several functions to be executed serially with results of each function
passed to next one. Arguments to resulting function before callback are passed to the first step.
Results of last function will be returned as a result of combined action.
Any error will be passed out immediately, stopping chain of execution.

```js
var pf = require('point-free');

var displayFile = pf.waterfall(
    fs.readFile,
    console.log.bind(console),
);

displayFile('filename.txt', callback)
```

When it's not possible to pass everything to first function `waterfall()` could be enclosed and either called immediately...:

```js
function copyFile(from, to, callback) {
    pf.waterfall(
        fs.readFile.bind(null, from),
        fs.writeFile.bind(null, to),
    )(callback);
}
```

... or passed to other combinator or decorator:

```js
pf.serial(
    action1,
    // Combined subtask
    pf.waterfall(
        fetchData,
        actOnIt
    ),
    // ...
)
```


### serial(funcs... | funcs)

### parallel(funcs... | funcs)

### manual(states)

```js
function cachedGet(url) {
    var filename = __dirname + '/cache/' + url.replace(/\//g, '#');

    return pf.manual({
        // always starts from 'start' state
        start: function (next) {
            fs.exists(filename, function (exists) {
                // go to some new state
                if (exists) next.readCache()
                else next.request();
            });
        },
        request: function (next) {
            // use state transition as callback
            request(url, next.writeCache);
        },
        readCache: function (next) {
            // use next.end to leave state machine
            fs.readFile(filename, 'utf-8', next.end);
        },
        writeCache: function (response, body, next) {
            fs.writeFile(filename, body, 'utf-8', function (error) {
                next.end(error, body);
            });
        }
    });
}

cachedGet('http://...')(function (err, body) {
    ...
})
```

### auto(jobs)

### while(test, body)

### doWhile(body, test)


## Primitives

### noop

A nice thing when you want to do something conditionally:

```js
pf.waterfall(
    jobs[id] ? pf.noop : pf.loadJob,
    // ...
)
```


### sleep(timeout)

```js
var delayedHandler = pf.waterfall(pf.sleep(1000), handler);
```


## Collections

### each(seq, func)

### map(seq, func)

### chunk(size, seq, func)

```js
// Insert links into database in chunks of 1000
pf.chunk(1000, links, function (chunk, callback) {
    db.insert('link', chunk).run(callback);
})(callback)
```


TODO:

- fill in docs
- comparison with async.js
{'fraction_non_alphanumeric': 0.139862148190695, 'fraction_numerical': 0.004595060310166571, 'mean_word_length': 2.8122605363984676, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1101144', 'n_tokens_mistral': 2133, 'n_tokens_neox': 2012, 'n_words': 672}