From 86dad525b1df592a68a7ade3f162c854aab340fb Mon Sep 17 00:00:00 2001 From: Ian McIntosh Date: Fri, 14 Jan 2022 10:58:47 -0500 Subject: [PATCH] Add Task.[tupledN, mapN] and concurrent variants. --- lib/src/task.dart | 60 +++++++++++++++++++++++++++++++++++++++++ test/task_test.dart | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/lib/src/task.dart b/lib/src/task.dart index 661f372..c8d9911 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -100,6 +100,66 @@ class Task implements MonadCatchOps { Task timeoutTo(Duration timeLimit, Task fallback) => timeout(timeLimit).redeemWith((err) => err is TimeoutException ? fallback : Task.failed(err), Task.value); Task get voided => replace(null); + + static Task> tupled2(Task a, Task b) => + a.flatMap((a) => b.map((b) => tuple2(a, b))); + + static Task> tupled3(Task a, Task b, Task c) => + tupled2(a, b).flatMap((ab) => c.map((c) => ab.append(c))); + + static Task> tupled4(Task a, Task b, Task c, Task d) => + tupled3(a, b, c).flatMap((abc) => d.map((d) => abc.append(d))); + + static Task> tupled5(Task a, Task b, Task c, Task d, Task e) => + tupled4(a, b, c, d).flatMap((abcd) => e.map((e) => abcd.append(e))); + + static Task> tupled6(Task a, Task b, Task c, Task d, Task e, Task f) => + tupled5(a, b, c, d, e).flatMap((abcde) => f.map((f) => abcde.append(f))); + + static Task map2(Task a, Task b, C Function(A a, B b) fn) => + tupled2(a, b).map((ab) => ab.apply(fn)); + + static Task map3(Task a, Task b, Task c, D Function(A a, B b, C c) fn) => + tupled3(a, b, c).map((abc) => abc.apply(fn)); + + static Task map4(Task a, Task b, Task c, Task d, E Function(A a, B b, C c, D d) fn) => + tupled4(a, b, c, d).map((abcd) => abcd.apply(fn)); + + static Task map5(Task a, Task b, Task c, Task d, Task e, F Function(A a, B b, C c, D d, E e) fn) => + tupled5(a, b, c, d, e).map((abcde) => abcde.apply(fn)); + + static Task map6(Task a, Task b, Task c, Task d, Task e, Task f, G Function(A a, B b, C c, D d, E e, F f) fn) => + tupled6(a, b, c, d, e, f).map((abcdef) => abcdef.apply(fn)); + + static Task> parTupled2(Task a, Task b) => + a.both(b); + + static Task> parTupled3(Task a, Task b, Task c) => + parTupled2(a, b).both(c).map((t) => t.value1.append(t.value2)); + + static Task> parTupled4(Task a, Task b, Task c, Task d) => + parTupled3(a, b, c).both(d).map((t) => t.value1.append(t.value2)); + + static Task> parTupled5(Task a, Task b, Task c, Task d, Task e) => + parTupled4(a, b, c, d).both(e).map((t) => t.value1.append(t.value2)); + + static Task> parTupled6(Task a, Task b, Task c, Task d, Task e, Task f) => + parTupled5(a, b, c, d, e).both(f).map((t) => t.value1.append(t.value2)); + + static Task parMap2(Task a, Task b, C Function(A a, B b) fn) => + parTupled2(a, b).map((ab) => ab.apply(fn)); + + static Task parMap3(Task a, Task b, Task c, D Function(A a, B b, C c) fn) => + parTupled3(a, b, c).map((abc) => abc.apply(fn)); + + static Task parMap4(Task a, Task b, Task c, Task d, E Function(A a, B b, C c, D d) fn) => + parTupled4(a, b, c, d).map((abcd) => abcd.apply(fn)); + + static Task parMap5(Task a, Task b, Task c, Task d, Task e, F Function(A a, B b, C c, D d, E e) fn) => + parTupled5(a, b, c, d, e).map((abcde) => abcde.apply(fn)); + + static Task parMap6(Task a, Task b, Task c, Task d, Task e, Task f, G Function(A a, B b, C c, D d, E e, F f) fn) => + parTupled6(a, b, c, d, e, f).map((abcdef) => abcdef.apply(fn)); } class TaskMonadCatch extends Functor with Applicative, Monad, MonadCatch { diff --git a/test/task_test.dart b/test/task_test.dart index 6ffad44..46d5376 100644 --- a/test/task_test.dart +++ b/test/task_test.dart @@ -320,4 +320,70 @@ void main() { expect(resultS, some(24)); expect(resultN, none()); }); + + test("Task.tupledN", () async { + Task t(int i) => Task.delay(() => i); + + final result = await Task.tupled3(t(0), t(1), t(2)).run(); + + expect(result, tuple3(0, 1, 2)); + }); + + test("Task.mapN", () async { + Task t(int i) => Task.delay(() => i); + + final sum = + await Task.map3(t(0), t(1), t(2), (int a, int b, int c) => a + b + c) + .run(); + + expect(sum, 3); + }); + + test("Task.tupledN is serial", () async { + Task t(int i) => + Task.delay(() => i).delayBy(const Duration(milliseconds: 500)); + + final result = await Task.tupled3(t(0), t(1), t(2)).timed.run(); + final elapsed = result.value1; + + expect(elapsed >= const Duration(milliseconds: 1500), true); + }); + + test("Task.parTupledN is concurrent", () async { + Task t(int i) => + Task.delay(() => i).delayBy(const Duration(milliseconds: 500)); + + final result = await Task.parTupled3(t(0), t(1), t(2)).timed.run(); + final elapsed = result.value1; + + expect(elapsed <= const Duration(milliseconds: 600), true); + }); + + test("Task.mapN is serial", () async { + Task t(int i) => + Task.delay(() => i).delayBy(const Duration(milliseconds: 500)); + + final result = + await Task.map3(t(0), t(1), t(2), (int a, int b, int c) => a + b + c) + .timed + .run(); + + final elapsed = result.value1; + + expect(elapsed >= const Duration(milliseconds: 1500), true); + }); + + test("Task.parMapN is concurrent", () async { + Task t(int i) => + Task.delay(() => i).delayBy(const Duration(milliseconds: 500)); + + final result = + await Task.parMap3(t(0), t(1), t(2), (int a, int b, int c) => a + b + c) + .timed + .run(); + + final elapsed = result.value1; + + expect(elapsed <= const Duration(seconds: 600), true); + }); }