Repository: plexis-js/plexis (js)

Issue:
Feature request: `compose`
Since most Plexis functions are pure we can create composed functions using closures.
`compose` works in a more functional way and provides users with flexibility, creating complex and tricky string operations. `compose` actually invokes a series of functions.

```javascript
@param {Function[]} [funcs] The functions to invoke.
@returns {Function} Returns the new composite function.
```


**Example usage**
```javascript
import compose from '@plexis/compose';
const inc = x => x + 1;
const add = (x, y) => x + y;
const ops = compose(inc, x => add(x, 2));

ops(1); // returns 4;
ops(10); // returns 13;
```

**Aliases**
```javascript
import compose from '@plexis/compose';
import {compose, _do} from 'plexis';
```


Pull request description:
Add `compose`
- Resolve #1 

Patch:
diff --git a/docs/pages/api.md b/docs/pages/api.md
index 8dbe685..65673dd 100644
--- a/docs/pages/api.md
+++ b/docs/pages/api.md
@@ -1,5 +1,6 @@
 ## API
 
+- [compose](/packages/compose)
 - [isLowerCase](/packages/isLowerCase)
 - [toLower](/packages/toLower)
 - [toPred](/packages/toPred)
diff --git a/packages/compose/.npmignore b/packages/compose/.npmignore
new file mode 100644
index 0000000..552cf85
--- /dev/null
+++ b/packages/compose/.npmignore
@@ -0,0 +1,17 @@
+.DS_Store
+*.test.js
+.prettierrc
+.github
+.template
+.package-template
+.nyc_output
+.circleci
+.commitlintrc.js
+coverage
+coverage.lcov
+docs
+babel.config.js
+test
+CONTRIBUTING.md
+CODE_OF_CONDUCT.md
+LICENCE
\ No newline at end of file
diff --git a/packages/compose/README.md b/packages/compose/README.md
new file mode 100644
index 0000000..cff1d26
--- /dev/null
+++ b/packages/compose/README.md
@@ -0,0 +1,28 @@
+# `@plexis/compose`
+
+Compose 2 or more functions.
+
+**Installation**
+`npm i @plexis/compose`
+
+**Usage**
+
+```javascript
+import compose from '@plexis/compose';
+const inc = x => x + 1;
+const add = (x, y) => x + y;
+const ops = compose(
+  inc,
+  x => add(x, 2)
+);
+
+ops(1); // returns 4;
+ops(10); // returns 13;
+```
+
+**Aliases**
+
+```javascript
+import compose from '@plexis/compose';
+import {compose, _do} from 'plexis';
+```
diff --git a/packages/compose/package.json b/packages/compose/package.json
new file mode 100644
index 0000000..8963c35
--- /dev/null
+++ b/packages/compose/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "@plexis/compose",
+  "version": "0.0.12",
+  "main": "dist/index.js",
+  "module": "dist/index.esm.js",
+  "jsnext:main": "./src/index.js",
+  "authors": [
+    "Nguyễn Tuấn Dũng <ntddebugger@gmail.com>"
+  ],
+  "license": "MIT",
+  "keywords": [
+    "utility",
+    "function"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/plexis-js/plexis.git"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/compose/src/index.js b/packages/compose/src/index.js
new file mode 100644
index 0000000..94b5274
--- /dev/null
+++ b/packages/compose/src/index.js
@@ -0,0 +1,18 @@
+const composeTwo = (f, g) => (...x) =>
+  Reflect.apply(f, undefined, [Reflect.apply(g, undefined, x)]);
+
+/**
+ * Compose two or more functions
+ * @param {Function[]} args Array of Function
+ * @returns {Function} Returns the new composite function.
+ * @example
+ * const inc = x => x + 1;
+ * const add = (x, y) => x + y;
+ * const ops = compose(inc, x => add(x, 2));
+ *
+ * ops(1); // returns 4;
+ * ops(10); // returns 13;
+ */
+const compose = (...args) => args.reduce(composeTwo);
+
+export default compose;
diff --git a/packages/plexis/package.json b/packages/plexis/package.json
index 5332826..d6d4193 100644
--- a/packages/plexis/package.json
+++ b/packages/plexis/package.json
@@ -9,6 +9,7 @@
   ],
   "license": "MIT",
   "dependencies": {
+    "@plexis/compose": "^0.0.12",
     "@plexis/to-lower": "^0.0.12",
     "@plexis/to-pred": "^0.0.12",
     "@plexis/to-succ": "^0.0.12",
diff --git a/packages/plexis/src/index.js b/packages/plexis/src/index.js
index 8bb84db..1ce1879 100644
--- a/packages/plexis/src/index.js
+++ b/packages/plexis/src/index.js
@@ -12,3 +12,4 @@ export {
   default as removeDiacritics
 } from '@plexis/without-diacritics';
 export {default as isLowerCase, default as isLower} from '@plexis/is-lowercase';
+export {default as compose, default as _do} from '@plexis/compose';


Test patch:
diff --git a/packages/compose/test/index.js b/packages/compose/test/index.js
new file mode 100644
index 0000000..fb62ffa
--- /dev/null
+++ b/packages/compose/test/index.js
@@ -0,0 +1,32 @@
+import compose from '../src';
+
+const inc = x => x + 1;
+const add = (x, y) => x + y;
+
+it('compose 2 unary functions', () => {
+  const ops = compose(
+    inc,
+    x => add(x, 2)
+  );
+  expect(ops(1)).toBe(4);
+  expect(ops(10)).toBe(13);
+});
+
+it('compose unary and binary functions', () => {
+  const ops = compose(
+    inc,
+    add
+  );
+  expect(ops(2, 3)).toBe(6);
+  expect(ops(-1, 10)).toBe(10);
+});
+
+it('compose 3 functions', () => {
+  const ops = compose(
+    inc,
+    inc,
+    x => add(x, 12)
+  );
+  expect(ops(2)).toBe(16);
+  expect(ops(10)).toBe(24);
+});
diff --git a/packages/plexis/test/__snapshots__/index.js.snap b/packages/plexis/test/__snapshots__/index.js.snap
index 139913b..fb10412 100644
--- a/packages/plexis/test/__snapshots__/index.js.snap
+++ b/packages/plexis/test/__snapshots__/index.js.snap
@@ -17,5 +17,7 @@ Array [
   "removeDiacritics",
   "isLowerCase",
   "isLower",
+  "compose",
+  "_do",
 ]
 `;