Skip to content

Commit 1be5dbd

Browse files
Add Either.transposeOption (#4466)
1 parent c445b8d commit 1be5dbd

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

.changeset/quiet-tables-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": minor
3+
---
4+
5+
Add Either.transposeOption

packages/effect/src/Either.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { TypeLambda } from "./HKT.js"
99
import type { Inspectable } from "./Inspectable.js"
1010
import * as doNotation from "./internal/doNotation.js"
1111
import * as either from "./internal/either.js"
12+
import * as option_ from "./internal/option.js"
1213
import type { Option } from "./Option.js"
1314
import type { Pipeable } from "./Pipeable.js"
1415
import type { Predicate, Refinement } from "./Predicate.js"
@@ -966,3 +967,38 @@ export {
966967
*/
967968
let_ as let
968969
}
970+
971+
/**
972+
* Converts an `Option` of an `Either` into an `Either` of an `Option`.
973+
*
974+
* **Details**
975+
*
976+
* This function transforms an `Option<Either<A, E>>` into an
977+
* `Either<Option<A>, E>`. If the `Option` is `None`, the resulting `Either`
978+
* will be a `Right` with a `None` value. If the `Option` is `Some`, the
979+
* inner `Either` will be executed, and its result wrapped in a `Some`.
980+
*
981+
* @example
982+
* ```ts
983+
* import { Effect, Either, Option } from "effect"
984+
*
985+
* // ┌─── Option<Either<number, never>>
986+
* // ▼
987+
* const maybe = Option.some(Either.right(42))
988+
*
989+
* // ┌─── Either<Option<number>, never, never>
990+
* // ▼
991+
* const result = Either.transposeOption(maybe)
992+
*
993+
* console.log(Effect.runSync(result))
994+
* // Output: { _id: 'Option', _tag: 'Some', value: 42 }
995+
* ```
996+
*
997+
* @since 3.14.0
998+
* @category Optional Wrapping & Unwrapping
999+
*/
1000+
export const transposeOption = <A = never, E = never>(
1001+
self: Option<Either<A, E>>
1002+
): Either<Option<A>, E> => {
1003+
return option_.isNone(self) ? right(option_.none) : map(self.value, option_.some)
1004+
}

packages/effect/test/Effect/optional-wrapping-unwrapping.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { assert, describe, it } from "@effect/vitest"
22
import * as Effect from "effect/Effect"
3+
import * as Either from "effect/Either"
34
import * as Option from "effect/Option"
45

56
describe("Effect", () => {
@@ -17,3 +18,19 @@ describe("Effect", () => {
1718
}))
1819
})
1920
})
21+
22+
describe("Either", () => {
23+
describe("transposeOption", () => {
24+
it.effect("None", () =>
25+
Effect.gen(function*() {
26+
const result = yield* Either.transposeOption(Option.none())
27+
assert.ok(Option.isNone(result))
28+
}))
29+
30+
it.effect("Some", () =>
31+
Effect.gen(function*() {
32+
const result = yield* Either.transposeOption(Option.some(Either.right(42)))
33+
assert.deepStrictEqual(result, Option.some(42))
34+
}))
35+
})
36+
})

0 commit comments

Comments
 (0)