Repository: gcanti/fp-ts
Pull request #563 (2018-09-13 08:24:36)

Make TaskEither a Monoid
The added functions are a combination of the Monoid definitions from `Task` and `Either`.


Patch:
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index be34bc27..34a2f08c 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -1,12 +1,21 @@
 import { Alt2 } from './Alt'
 import { Bifunctor2 } from './Bifunctor'
-import { Either, fromPredicate as eitherFromPredicate, left as eitherLeft, right as eitherRight } from './Either'
+import {
+  Either,
+  fromPredicate as eitherFromPredicate,
+  left as eitherLeft,
+  right as eitherRight,
+  getSemigroup as eitherGetSemigroup,
+  getApplySemigroup as eitherGetApplySemigroup
+} from './Either'
 import * as eitherT from './EitherT'
 import { IO } from './IO'
 import { Monad2 } from './Monad'
-import { Task, fromIO as taskFromIO, task, tryCatch as taskTryCatch } from './Task'
+import { Task, fromIO as taskFromIO, task, tryCatch as taskTryCatch, getSemigroup as taskGetSemigroup } from './Task'
 import { Lazy, constIdentity, constant, Predicate } from './function'
 import { IOEither } from './IOEither'
+import { Semigroup } from './Semigroup'
+import { Monoid } from './Monoid'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -174,6 +183,39 @@ export const fromPredicate = <L, A>(predicate: Predicate<A>, whenFalse: (a: A) =
   return a => fromEither(f(a))
 }
 
+/**
+ * @function
+ * @since 1.9.0
+ */
+export const getSemigroup = <L, A>(S: Semigroup<A>): Semigroup<TaskEither<L, A>> => {
+  const S2 = taskGetSemigroup(eitherGetSemigroup<L, A>(S))
+  return {
+    concat: (x, y) => new TaskEither<L, A>(S2.concat(x.value, y.value))
+  }
+}
+
+/**
+ * @function
+ * @since 1.9.0
+ */
+export const getApplySemigroup = <L, A>(S: Semigroup<A>): Semigroup<TaskEither<L, A>> => {
+  const S2 = taskGetSemigroup(eitherGetApplySemigroup<L, A>(S))
+  return {
+    concat: (x, y) => new TaskEither<L, A>(S2.concat(x.value, y.value))
+  }
+}
+
+/**
+ * @function
+ * @since 1.9.0
+ */
+export const getApplyMonoid = <L, A>(M: Monoid<A>): Monoid<TaskEither<L, A>> => {
+  return {
+    ...getApplySemigroup(M),
+    empty: of(M.empty)
+  }
+}
+
 /**
  * @function
  * @since 1.0.0


Test patch:
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index 42d4b6bc..fae64fa7 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -1,7 +1,7 @@
 import * as assert from 'assert'
 import { left as eitherLeft, right as eitherRight } from '../src/Either'
 import { IO } from '../src/IO'
-import { Task, task } from '../src/Task'
+import { Task, task, delay } from '../src/Task'
 import {
   TaskEither,
   fromIO,
@@ -12,9 +12,13 @@ import {
   taskify,
   tryCatch,
   fromIOEither,
-  fromPredicate
+  fromPredicate,
+  getApplyMonoid,
+  getSemigroup
 } from '../src/TaskEither'
 import { IOEither } from '../src/IOEither'
+import { monoidString } from '../src/Monoid'
+import { semigroupSum } from '../src/Semigroup'
 
 describe('TaskEither', () => {
   it('ap', () => {
@@ -211,4 +215,47 @@ describe('TaskEither', () => {
       assert.deepEqual(e2, eitherLeft('Invalid number 1'))
     })
   })
+
+  it('getSemigroup', () => {
+    const S = getSemigroup<string, number>(semigroupSum)
+    return Promise.all([
+      S.concat(left(delay(10, 'a')), left(delay(10, 'b')))
+        .run()
+        .then(x => assert.deepEqual(x, eitherLeft('a'))),
+      S.concat(left(delay(10, 'a')), right(delay(10, 2)))
+        .run()
+        .then(x => assert.deepEqual(x, eitherRight(2))),
+      S.concat(right(delay(10, 1)), left(delay(10, 'b')))
+        .run()
+        .then(x => assert.deepEqual(x, eitherRight(1))),
+      S.concat(right(delay(10, 1)), right(delay(10, 2)))
+        .run()
+        .then(x => assert.deepEqual(x, eitherRight(3)))
+    ])
+  })
+
+  describe('getApplyMonoid', () => {
+    const M = getApplyMonoid(monoidString)
+
+    it('concat (right)', () => {
+      return M.concat(right(delay(10, 'a')), right(delay(10, 'b')))
+        .run()
+        .then(x => assert.deepEqual(x, eitherRight('ab')))
+    })
+    it('concat (left)', () => {
+      return M.concat(right(delay(10, 'a')), left(delay(10, 'b')))
+        .run()
+        .then(x => assert.deepEqual(x, eitherLeft('b')))
+    })
+    it('empty (right)', () => {
+      return M.concat(right(delay(10, 'a')), M.empty)
+        .run()
+        .then(x => assert.deepEqual(x, eitherRight('a')))
+    })
+    it('empty (left)', () => {
+      return M.concat(M.empty, right(delay(10, 'a')))
+        .run()
+        .then(x => assert.deepEqual(x, eitherRight('a')))
+    })
+  })
 })