diff --git a/.changeset/rotten-beers-nail.md b/.changeset/rotten-beers-nail.md new file mode 100644 index 0000000..16ac80c --- /dev/null +++ b/.changeset/rotten-beers-nail.md @@ -0,0 +1,5 @@ +--- +"ftld": minor +--- + +`Do` now recursively unwraps monadic value similar to async await diff --git a/lib/src/do.test.ts b/lib/src/do.test.ts index b0c74c0..c53b6eb 100644 --- a/lib/src/do.test.ts +++ b/lib/src/do.test.ts @@ -385,9 +385,20 @@ describe("Do", () => { it("should unwrap nested monads", async () => { const task = Do(function* ($) { - return yield* $(Result.from(() => Option.Some(1))); + const x = yield* $( + Promise.resolve( + Result.from(() => + Option.Some( + Result.from(() => + Option.Some(Task.from(() => Promise.resolve(1))) + ) + ) + ) + ) + ); + return x + 3; }); - expect(task.run()).toEqual(Result.Ok(1)); + expect(await task.run()).toEqual(Result.Ok(4)); }); }); diff --git a/lib/src/do.ts b/lib/src/do.ts index 15007b4..7433617 100644 --- a/lib/src/do.ts +++ b/lib/src/do.ts @@ -9,6 +9,7 @@ import { isMonad, } from "./utils.js"; import { Task } from "./task.js"; +import { isPromise } from "./internals.js"; class Gen implements Generator { called = false; @@ -83,11 +84,14 @@ const toTask = (maybeGen: unknown): Task => { const value = maybeGen instanceof UnwrapGen ? maybeGen.value : maybeGen; const onErr = maybeGen instanceof UnwrapGen ? maybeGen.onErr ?? identity : identity; - return ( - value instanceof Task - ? value - : Task.from(() => (isMonad(value) ? value.unwrap() : value), identity) - ).mapErr(onErr); + const unwrap = (value: unknown): unknown => { + return isMonad(value) + ? unwrap(value.unwrap()) + : isPromise(value) + ? value.then(unwrap) + : value; + }; + return Task.from(() => unwrap(value), identity).mapErr(onErr); }; type ComputeTask = Gen extends Array< @@ -131,7 +135,7 @@ type EnsureGenUnwrapped = Gen extends UnwrapGen ? T : Gen; type UnwrapValue = [A] extends [never] ? never : A extends Monad - ? B + ? UnwrapValue : A extends Promise ? UnwrapValue : A extends (...args: any) => infer B