diff --git a/.changeset/quiet-tables-listen.md b/.changeset/quiet-tables-listen.md new file mode 100644 index 00000000000..9a91f4b6521 --- /dev/null +++ b/.changeset/quiet-tables-listen.md @@ -0,0 +1,5 @@ +--- +"effect": minor +--- + +Add Either.transposeOption diff --git a/packages/effect/src/Either.ts b/packages/effect/src/Either.ts index 6b5d15ff6c6..ed57dd6c8b7 100644 --- a/packages/effect/src/Either.ts +++ b/packages/effect/src/Either.ts @@ -9,6 +9,7 @@ import type { TypeLambda } from "./HKT.js" import type { Inspectable } from "./Inspectable.js" import * as doNotation from "./internal/doNotation.js" import * as either from "./internal/either.js" +import * as option_ from "./internal/option.js" import type { Option } from "./Option.js" import type { Pipeable } from "./Pipeable.js" import type { Predicate, Refinement } from "./Predicate.js" @@ -966,3 +967,38 @@ export { */ let_ as let } + +/** + * Converts an `Option` of an `Either` into an `Either` of an `Option`. + * + * **Details** + * + * This function transforms an `Option>` into an + * `Either, E>`. If the `Option` is `None`, the resulting `Either` + * will be a `Right` with a `None` value. If the `Option` is `Some`, the + * inner `Either` will be executed, and its result wrapped in a `Some`. + * + * @example + * ```ts + * import { Effect, Either, Option } from "effect" + * + * // ┌─── Option> + * // ▼ + * const maybe = Option.some(Either.right(42)) + * + * // ┌─── Either, never, never> + * // ▼ + * const result = Either.transposeOption(maybe) + * + * console.log(Effect.runSync(result)) + * // Output: { _id: 'Option', _tag: 'Some', value: 42 } + * ``` + * + * @since 3.14.0 + * @category Optional Wrapping & Unwrapping + */ +export const transposeOption = ( + self: Option> +): Either, E> => { + return option_.isNone(self) ? right(option_.none) : map(self.value, option_.some) +} diff --git a/packages/effect/test/Effect/optional-wrapping-unwrapping.test.ts b/packages/effect/test/Effect/optional-wrapping-unwrapping.test.ts index 2b86df1103b..de19644ef9f 100644 --- a/packages/effect/test/Effect/optional-wrapping-unwrapping.test.ts +++ b/packages/effect/test/Effect/optional-wrapping-unwrapping.test.ts @@ -1,5 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import * as Effect from "effect/Effect" +import * as Either from "effect/Either" import * as Option from "effect/Option" describe("Effect", () => { @@ -17,3 +18,19 @@ describe("Effect", () => { })) }) }) + +describe("Either", () => { + describe("transposeOption", () => { + it.effect("None", () => + Effect.gen(function*() { + const result = yield* Either.transposeOption(Option.none()) + assert.ok(Option.isNone(result)) + })) + + it.effect("Some", () => + Effect.gen(function*() { + const result = yield* Either.transposeOption(Option.some(Either.right(42))) + assert.deepStrictEqual(result, Option.some(42)) + })) + }) +})