diff --git a/README.md b/README.md
index 06d2772..7186503 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,8 @@ structures:
* [Setoid](#setoid)
* [Ord](#ord)
+* [Semigroupoid](#semigroupoid)
+* [Category](#category)
* [Semigroup](#semigroup)
* [Monoid](#monoid)
* [Functor](#functor)
@@ -30,7 +32,7 @@ structures:
* [Bifunctor](#bifunctor)
* [Profunctor](#profunctor)
-
+
## General
@@ -209,6 +211,54 @@ A value which has an Ord must provide a `lte` method. The
2. `lte` must return a boolean (`true` or `false`).
+### Semigroupoid
+
+1. `a.compose(b.compose(c)) === a.compose(b).compose(c)` (associativity)
+
+#### `compose` method
+
+```hs
+compose :: Semigroupoid c => c i j ~> c j k -> c i k
+```
+
+A value which has a Semigroupoid must provide a `compose` method. The
+`compose` method takes one argument:
+
+ a.compose(b)
+
+1. `b` must be a value of the same Semigroupoid
+
+ 1. If `b` is not the same semigroupoid, behaviour of `compose` is
+ unspecified.
+
+2. `compose` must return a value of the same Semigroupoid.
+
+### Category
+
+A value that implements the Category specification must also implement
+the [Semigroupoid](#semigroupoid) specification.
+
+1. `a.compose(C.id())` is equivalent to `a` (right identity)
+2. `C.id().compose(a)` is equivalent to `a` (left identity)
+
+#### `id` method
+
+```hs
+id :: Category c => () -> c a a
+```
+
+A value which has a Category must provide an `id` function on its
+[type representative](#type-representatives):
+
+ C.id()
+
+Given a value `c`, one can access its type representative via the
+`constructor` property:
+
+ c.constructor.id()
+
+1. `id` must return a value of the same Category
+
### Semigroup
1. `a.concat(b).concat(c)` is equivalent to `a.concat(b.concat(c))` (associativity)
diff --git a/figures/dependencies.dot b/figures/dependencies.dot
index 38e84a9..cc5122b 100644
--- a/figures/dependencies.dot
+++ b/figures/dependencies.dot
@@ -7,6 +7,7 @@ digraph {
Applicative;
Apply;
Bifunctor;
+ Category;
Chain;
ChainRec;
Comonad;
@@ -20,6 +21,7 @@ digraph {
Plus;
Profunctor;
Semigroup;
+ Semigroupoid;
Setoid;
Traversable;
@@ -41,5 +43,6 @@ digraph {
Functor -> Traversable;
Plus -> Alternative;
Semigroup -> Monoid;
+ Semigroupoid -> Category;
Setoid -> Ord;
}
diff --git a/figures/dependencies.png b/figures/dependencies.png
index 90c2e6c..598c70a 100644
Binary files a/figures/dependencies.png and b/figures/dependencies.png differ
diff --git a/index.js b/index.js
index 32c8881..14415f3 100644
--- a/index.js
+++ b/index.js
@@ -8,6 +8,8 @@
var mapping = {
equals: 'fantasy-land/equals',
lte: 'fantasy-land/lte',
+ compose: 'fantasy-land/compose',
+ id: 'fantasy-land/id',
concat: 'fantasy-land/concat',
empty: 'fantasy-land/empty',
map: 'fantasy-land/map',
diff --git a/internal/patch.js b/internal/patch.js
index 8a630a9..b7403a1 100644
--- a/internal/patch.js
+++ b/internal/patch.js
@@ -30,4 +30,16 @@ module.exports = () => {
return this.concat(b);
};
Array[fl.zero] = () => [];
+
+ Function.prototype[fl.compose] = function(g) {
+ const f = this;
+ return function(x) {
+ return f(g(x));
+ };
+ };
+ Function[fl.id] = function() {
+ return function(x) {
+ return x;
+ };
+ };
};
diff --git a/laws/category.js b/laws/category.js
new file mode 100644
index 0000000..422bdb8
--- /dev/null
+++ b/laws/category.js
@@ -0,0 +1,26 @@
+'use strict';
+
+const {compose, id} = require('..');
+
+/**
+
+ ### Category
+
+ 1. `a.compose(C.id())` is equivalent to `a` (right identity)
+ 2. `C.id().compose(a)` is equivalent to `a` (left identity)
+
+**/
+
+const leftIdentity = f => eq => x => {
+ const a = f[compose](Function[id]())(x);
+ const b = f(x);
+ return eq(a, b);
+};
+
+const rightIdentity = f => eq => x => {
+ const a = Function[id]()[compose](f)(x);
+ const b = f(x);
+ return eq(a, b);
+};
+
+module.exports = {leftIdentity, rightIdentity};
diff --git a/laws/semigroupoid.js b/laws/semigroupoid.js
new file mode 100644
index 0000000..325b109
--- /dev/null
+++ b/laws/semigroupoid.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const {compose} = require('..');
+
+/**
+
+ ### Semigroupoid
+
+ 1. `a.compose(b).compose(c)` is equivalent to `a.compose(b.compose(c))` (associativity)
+
+**/
+
+const associativity = f => g => h => eq => x => {
+ const a = f[compose](g)[compose](h)(x);
+ const b = f[compose](g[compose](h))(x);
+ return eq(a, b);
+};
+
+module.exports = {associativity};
diff --git a/test.js b/test.js
index f5a70c9..0b3fb8a 100644
--- a/test.js
+++ b/test.js
@@ -19,6 +19,8 @@ const monoid = require('./laws/monoid');
const ord = require('./laws/ord');
const plus = require('./laws/plus');
const semigroup = require('./laws/semigroup');
+const semigroupoid = require('./laws/semigroupoid');
+const category = require('./laws/category');
const setoid = require('./laws/setoid');
const traversable = require('./laws/traversable');
@@ -51,6 +53,11 @@ exports.apply = {
composition: test(apply.composition(Id)(equality)),
};
+exports.category = {
+ leftIdentity: test(category.leftIdentity(x => x + 1)(equality)),
+ rightIdentity: test(category.rightIdentity(x => x + 1)(equality)),
+};
+
exports.chain = {
associativity: test(chain.associativity(Id)(equality)),
};
@@ -109,6 +116,10 @@ exports.semigroup = {
associativity: test(semigroup.associativity(Id[fl.of])(equality)),
};
+exports.semigroupoid = {
+ associativity: semigroupoid.associativity(x => x + 1)(x => x * x)(x => x - 2)(equality)(5),
+};
+
exports.setoid = {
reflexivity: test(setoid.reflexivity(Id[fl.of])(equality)),
symmetry: test(setoid.symmetry(Id[fl.of])(equality)),