Repository: gcanti/fp-ts
Pull request #1051 (2019-12-14 13:08:12)

Add `getMonoid` for Ord
Following https://github.com/gcanti/fp-ts/pull/1046, I think it would be semantically consistent to add a `Monoid<Ord>` instance, with `empty` returning an `Ord` instance which always considers compared elements equal (Js now guarantees to not to change order of elements in such a case - "no sorting applied" - sorting in js is now guaranteed to be stable). Besides consistency with `sortBy` implementation which can now take empty array of ords as input (and then performs "no sorting at all" - data remains untouched) it honestly... makes `sortBy` unnecessary (because we can now always build a specific `Ord` first, using `fold`), which leaves us with nice, terse and consistent interface for sorting, made of one function only - `sort` itself.


gcanti: Thanks for this PR @vicrac

> Js now guarantees to not to change order of elements in such a case - "no sorting applied" - sorting in js is now guaranteed to be stable

Actually it doesn't matter, `fromCompare(() => 0)` is the monoidal unit whether sorting is stable or not

Patch:
diff --git a/src/Ord.ts b/src/Ord.ts
index 38ab57c7..3a24dcbe 100644
--- a/src/Ord.ts
+++ b/src/Ord.ts
@@ -16,6 +16,7 @@ import { Semigroup } from './Semigroup'
 import { Eq } from './Eq'
 import { Contravariant1 } from './Contravariant'
 import { pipeable } from './pipeable'
+import { Monoid } from './Monoid'
 
 declare module './HKT' {
   interface URItoKind<A> {
@@ -226,6 +227,18 @@ export function getSemigroup<A = never>(): Semigroup<Ord<A>> {
   }
 }
 
+const empty = fromCompare(() => 0)
+
+/**
+ * @since 2.4.0
+ */
+export function getMonoid<A = never>(): Monoid<Ord<A>> {
+  return {
+    empty,
+    concat: getSemigroup<A>().concat
+  }
+}
+
 /**
  * Given a tuple of `Ord`s returns an `Ord` for the tuple
  *


Test patch:
diff --git a/test/Ord.ts b/test/Ord.ts
index 78b520ee..fe8d4e56 100644
--- a/test/Ord.ts
+++ b/test/Ord.ts
@@ -6,6 +6,7 @@ import {
   clamp,
   getDualOrd,
   getSemigroup,
+  getMonoid,
   ordDate,
   ordNumber,
   ordString,
@@ -15,6 +16,7 @@ import {
   getTupleOrd,
   ordBoolean
 } from '../src/Ord'
+import { fold } from '../src/Monoid'
 
 describe('Ord', () => {
   it('getTupleOrd', () => {
@@ -51,6 +53,33 @@ describe('Ord', () => {
     ])
   })
 
+  it('getMonoid', () => {
+    interface Person {
+      name: string
+      age: number
+    }
+    const people: Person[] = [
+      { name: 'John', age: 42 },
+      { name: 'Dorothy', age: 37 },
+      { name: 'John', age: 37 }
+    ]
+
+    const sortByName = ord.contramap(ordString, (p: Person) => p.name)
+    const sortByAge = ord.contramap(ordNumber, (p: Person) => p.age)
+
+    const monoid = getMonoid<Person>()
+    const sortByNameByAge = fold(monoid)([sortByName, sortByAge])
+    const dontSort = fold(monoid)([])
+
+    assert.deepStrictEqual(sort(sortByNameByAge)(people), [
+      { name: 'Dorothy', age: 37 },
+      { name: 'John', age: 37 },
+      { name: 'John', age: 42 }
+    ])
+
+    assert.deepStrictEqual(sort(dontSort)(people), people)
+  })
+
   it('ordNumber', () => {
     assert.strictEqual(ordNumber.compare(1, 2), -1)
     assert.strictEqual(ordNumber.compare(2, 1), 1)