Skip to content

Commit 16a9a83

Browse files
LaureRCeffect-bot
authored andcommitted
Add Effect.transposeMapOption (#4597)
1 parent 84b8ed7 commit 16a9a83

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed

.changeset/small-ties-love.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 Effect.transposeMapOption

packages/effect/dtslint/Effect.tst.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,4 +1392,37 @@ describe("Effect", () => {
13921392
Effect.Effect<Option.Option<string>, "err-1", "dep-1">
13931393
>()
13941394
})
1395+
1396+
it("transposeMapOption", () => {
1397+
expect(Effect.transposeMapOption(Option.none(), (value) => {
1398+
expect(value).type.toBe<never>()
1399+
return string
1400+
})).type.toBe<
1401+
Effect.Effect<Option.Option<string>, "err-1", "dep-1">
1402+
>()
1403+
expect(pipe(
1404+
Option.none(),
1405+
Effect.transposeMapOption((value) => {
1406+
expect(value).type.toBe<never>()
1407+
return string
1408+
})
1409+
)).type.toBe<
1410+
Effect.Effect<Option.Option<string>, "err-1", "dep-1">
1411+
>()
1412+
expect(Effect.transposeMapOption(Option.some(42), (value) => {
1413+
expect(value).type.toBe<number>()
1414+
return string
1415+
})).type.toBe<
1416+
Effect.Effect<Option.Option<string>, "err-1", "dep-1">
1417+
>()
1418+
expect(pipe(
1419+
Option.some(42),
1420+
Effect.transposeMapOption((value) => {
1421+
expect(value).type.toBe<number>()
1422+
return string
1423+
})
1424+
)).type.toBe<
1425+
Effect.Effect<Option.Option<string>, "err-1", "dep-1">
1426+
>()
1427+
})
13951428
})

packages/effect/src/Effect.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13068,6 +13068,50 @@ export const transposeOption = <A = never, E = never, R = never>(
1306813068
return option_.isNone(self) ? succeedNone : map(self.value, option_.some)
1306913069
}
1307013070

13071+
/**
13072+
* Applies an `Effect` on an `Option` and transposes the result.
13073+
*
13074+
* **Details**
13075+
*
13076+
* If the `Option` is `None`, the resulting `Effect` will immediately succeed with a `None` value.
13077+
* If the `Option` is `Some`, the effectful operation will be executed on the inner value, and its result wrapped in a `Some`.
13078+
*
13079+
* @example
13080+
* ```ts
13081+
* import { Effect, Option, pipe } from "effect"
13082+
*
13083+
* // ┌─── Effect<Option<number>, never, never>>
13084+
* // ▼
13085+
* const noneResult = pipe(
13086+
* Option.none(),
13087+
* Effect.transposeMapOption(() => Effect.succeed(42)) // will not be executed
13088+
* )
13089+
* console.log(Effect.runSync(noneResult))
13090+
* // Output: { _id: 'Option', _tag: 'None' }
13091+
*
13092+
* // ┌─── Effect<Option<number>, never, never>>
13093+
* // ▼
13094+
* const someSuccessResult = pipe(
13095+
* Option.some(42),
13096+
* Effect.transposeMapOption((value) => Effect.succeed(value * 2))
13097+
* )
13098+
* console.log(Effect.runSync(someSuccessResult))
13099+
* // Output: { _id: 'Option', _tag: 'Some', value: 84 }
13100+
* ```
13101+
*
13102+
* @since 3.14.0
13103+
* @category Optional Wrapping & Unwrapping
13104+
*/
13105+
export const transposeMapOption = dual<
13106+
<A, B, E = never, R = never>(
13107+
f: (self: A) => Effect<B, E, R>
13108+
) => (self: Option.Option<A>) => Effect<Option.Option<B>, E, R>,
13109+
<A, B, E = never, R = never>(
13110+
self: Option.Option<A>,
13111+
f: (self: A) => Effect<B, E, R>
13112+
) => Effect<Option.Option<B>, E, R>
13113+
>(2, (self, f) => option_.isNone(self) ? succeedNone : map(f(self.value), option_.some))
13114+
1307113115
/**
1307213116
* @since 2.0.0
1307313117
* @category Models

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { assert, describe, it } from "@effect/vitest"
22
import * as Effect from "effect/Effect"
33
import * as Either from "effect/Either"
4+
import { pipe } from "effect/Function"
45
import * as Option from "effect/Option"
56

67
describe("Effect", () => {
@@ -17,6 +18,66 @@ describe("Effect", () => {
1718
assert.deepStrictEqual(result, Option.some(42))
1819
}))
1920
})
21+
describe("transposeMapOption", () => {
22+
describe("None", () => {
23+
it.effect("Success", () =>
24+
Effect.gen(function*() {
25+
const resultDataFirst = yield* Effect.transposeMapOption(Option.none(), () => Effect.succeed(42))
26+
assert.ok(Option.isNone(resultDataFirst))
27+
28+
const resultDataLast = yield* pipe(
29+
Option.none(),
30+
Effect.transposeMapOption(() => Effect.succeed(42))
31+
)
32+
assert.ok(Option.isNone(resultDataLast))
33+
}))
34+
it.effect("Failure", () =>
35+
Effect.gen(function*() {
36+
const resultDataFirst = yield* Effect.transposeMapOption(Option.none(), () => Effect.fail("Error"))
37+
assert.ok(Option.isNone(resultDataFirst))
38+
39+
const resultDataLast = yield* pipe(
40+
Option.none(),
41+
Effect.transposeMapOption(() => Effect.fail("Error"))
42+
)
43+
assert.ok(Option.isNone(resultDataLast))
44+
}))
45+
})
46+
47+
describe("Some", () => {
48+
describe("None", () => {
49+
it.effect("Success", () =>
50+
Effect.gen(function*() {
51+
const resultDataFirst = yield* Effect.transposeMapOption(Option.some(42), (value) =>
52+
Effect.succeed(value * 2))
53+
assert.deepStrictEqual(resultDataFirst, Option.some(84))
54+
55+
const resultDataLast = yield* pipe(
56+
Option.some(42),
57+
Effect.transposeMapOption((value) =>
58+
Effect.succeed(value * 2)
59+
)
60+
)
61+
assert.deepStrictEqual(resultDataLast, Option.some(84))
62+
}))
63+
it.effect("Failure", () =>
64+
Effect.gen(function*() {
65+
const resultDataFirst = yield* pipe(
66+
Effect.transposeMapOption(Option.some(42), () => Effect.fail("error")),
67+
Effect.flip
68+
)
69+
assert.equal(resultDataFirst, "error")
70+
71+
const resultDataLast = yield* pipe(
72+
Option.some(42),
73+
Effect.transposeMapOption(() => Effect.fail("error")),
74+
Effect.flip
75+
)
76+
assert.equal(resultDataLast, "error")
77+
}))
78+
})
79+
})
80+
})
2081
})
2182

2283
describe("Either", () => {

0 commit comments

Comments
 (0)