Skip to content

Commit 3d48842

Browse files
Merge pull request #248 from gabejohnson/add-semigroupoid-and-category
Add Semigroupoid and Category algebras
2 parents 5bcc74d + d82076a commit 3d48842

File tree

8 files changed

+124
-1
lines changed

8 files changed

+124
-1
lines changed

README.md

+51-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ structures:
1111

1212
* [Setoid](#setoid)
1313
* [Ord](#ord)
14+
* [Semigroupoid](#semigroupoid)
15+
* [Category](#category)
1416
* [Semigroup](#semigroup)
1517
* [Monoid](#monoid)
1618
* [Functor](#functor)
@@ -30,7 +32,7 @@ structures:
3032
* [Bifunctor](#bifunctor)
3133
* [Profunctor](#profunctor)
3234

33-
<img src="figures/dependencies.png" width="888" height="294" />
35+
<img src="figures/dependencies.png" width="888" height="257" />
3436

3537
## General
3638

@@ -209,6 +211,54 @@ A value which has an Ord must provide a `lte` method. The
209211

210212
2. `lte` must return a boolean (`true` or `false`).
211213

214+
### Semigroupoid
215+
216+
1. `a.compose(b.compose(c)) === a.compose(b).compose(c)` (associativity)
217+
218+
#### `compose` method
219+
220+
```hs
221+
compose :: Semigroupoid c => c i j ~> c j k -> c i k
222+
```
223+
224+
A value which has a Semigroupoid must provide a `compose` method. The
225+
`compose` method takes one argument:
226+
227+
a.compose(b)
228+
229+
1. `b` must be a value of the same Semigroupoid
230+
231+
1. If `b` is not the same semigroupoid, behaviour of `compose` is
232+
unspecified.
233+
234+
2. `compose` must return a value of the same Semigroupoid.
235+
236+
### Category
237+
238+
A value that implements the Category specification must also implement
239+
the [Semigroupoid](#semigroupoid) specification.
240+
241+
1. `a.compose(C.id())` is equivalent to `a` (right identity)
242+
2. `C.id().compose(a)` is equivalent to `a` (left identity)
243+
244+
#### `id` method
245+
246+
```hs
247+
id :: Category c => () -> c a a
248+
```
249+
250+
A value which has a Category must provide an `id` function on its
251+
[type representative](#type-representatives):
252+
253+
C.id()
254+
255+
Given a value `c`, one can access its type representative via the
256+
`constructor` property:
257+
258+
c.constructor.id()
259+
260+
1. `id` must return a value of the same Category
261+
212262
### Semigroup
213263

214264
1. `a.concat(b).concat(c)` is equivalent to `a.concat(b.concat(c))` (associativity)

figures/dependencies.dot

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ digraph {
77
Applicative;
88
Apply;
99
Bifunctor;
10+
Category;
1011
Chain;
1112
ChainRec;
1213
Comonad;
@@ -20,6 +21,7 @@ digraph {
2021
Plus;
2122
Profunctor;
2223
Semigroup;
24+
Semigroupoid;
2325
Setoid;
2426
Traversable;
2527

@@ -41,5 +43,6 @@ digraph {
4143
Functor -> Traversable;
4244
Plus -> Alternative;
4345
Semigroup -> Monoid;
46+
Semigroupoid -> Category;
4447
Setoid -> Ord;
4548
}

figures/dependencies.png

3.67 KB
Loading

index.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
var mapping = {
99
equals: 'fantasy-land/equals',
1010
lte: 'fantasy-land/lte',
11+
compose: 'fantasy-land/compose',
12+
id: 'fantasy-land/id',
1113
concat: 'fantasy-land/concat',
1214
empty: 'fantasy-land/empty',
1315
map: 'fantasy-land/map',

internal/patch.js

+12
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,16 @@ module.exports = () => {
3030
return this.concat(b);
3131
};
3232
Array[fl.zero] = () => [];
33+
34+
Function.prototype[fl.compose] = function(g) {
35+
const f = this;
36+
return function(x) {
37+
return f(g(x));
38+
};
39+
};
40+
Function[fl.id] = function() {
41+
return function(x) {
42+
return x;
43+
};
44+
};
3345
};

laws/category.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
const {compose, id} = require('..');
4+
5+
/**
6+
7+
### Category
8+
9+
1. `a.compose(C.id())` is equivalent to `a` (right identity)
10+
2. `C.id().compose(a)` is equivalent to `a` (left identity)
11+
12+
**/
13+
14+
const leftIdentity = f => eq => x => {
15+
const a = f[compose](Function[id]())(x);
16+
const b = f(x);
17+
return eq(a, b);
18+
};
19+
20+
const rightIdentity = f => eq => x => {
21+
const a = Function[id]()[compose](f)(x);
22+
const b = f(x);
23+
return eq(a, b);
24+
};
25+
26+
module.exports = {leftIdentity, rightIdentity};

laws/semigroupoid.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
3+
const {compose} = require('..');
4+
5+
/**
6+
7+
### Semigroupoid
8+
9+
1. `a.compose(b).compose(c)` is equivalent to `a.compose(b.compose(c))` (associativity)
10+
11+
**/
12+
13+
const associativity = f => g => h => eq => x => {
14+
const a = f[compose](g)[compose](h)(x);
15+
const b = f[compose](g[compose](h))(x);
16+
return eq(a, b);
17+
};
18+
19+
module.exports = {associativity};

test.js

+11
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const monoid = require('./laws/monoid');
1919
const ord = require('./laws/ord');
2020
const plus = require('./laws/plus');
2121
const semigroup = require('./laws/semigroup');
22+
const semigroupoid = require('./laws/semigroupoid');
23+
const category = require('./laws/category');
2224
const setoid = require('./laws/setoid');
2325
const traversable = require('./laws/traversable');
2426

@@ -51,6 +53,11 @@ exports.apply = {
5153
composition: test(apply.composition(Id)(equality)),
5254
};
5355

56+
exports.category = {
57+
leftIdentity: test(category.leftIdentity(x => x + 1)(equality)),
58+
rightIdentity: test(category.rightIdentity(x => x + 1)(equality)),
59+
};
60+
5461
exports.chain = {
5562
associativity: test(chain.associativity(Id)(equality)),
5663
};
@@ -109,6 +116,10 @@ exports.semigroup = {
109116
associativity: test(semigroup.associativity(Id[fl.of])(equality)),
110117
};
111118

119+
exports.semigroupoid = {
120+
associativity: semigroupoid.associativity(x => x + 1)(x => x * x)(x => x - 2)(equality)(5),
121+
};
122+
112123
exports.setoid = {
113124
reflexivity: test(setoid.reflexivity(Id[fl.of])(equality)),
114125
symmetry: test(setoid.symmetry(Id[fl.of])(equality)),

0 commit comments

Comments
 (0)