Skip to content

Commit 6bf15ff

Browse files
gcantieffect-bot
authored andcommitted
Add Effect.transposeOption, closes #3142 (#4284)
1 parent 91f753e commit 6bf15ff

File tree

4 files changed

+99
-2
lines changed

4 files changed

+99
-2
lines changed

.changeset/cyan-radios-relate.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
"effect": minor
3+
---
4+
5+
Add `Effect.transposeOption`, closes #3142.
6+
7+
Converts an `Option` of an `Effect` into an `Effect` of an `Option`.
8+
9+
**Details**
10+
11+
This function transforms an `Option<Effect<A, E, R>>` into an
12+
`Effect<Option<A>, E, R>`. If the `Option` is `None`, the resulting `Effect`
13+
will immediately succeed with a `None` value. If the `Option` is `Some`, the
14+
inner `Effect` will be executed, and its result wrapped in a `Some`.
15+
16+
**Example**
17+
18+
```ts
19+
import { Effect, Option } from "effect"
20+
21+
// ┌─── Option<Effect<number, never, never>>
22+
//
23+
const maybe = Option.some(Effect.succeed(42))
24+
25+
// ┌─── Effect<Option<number>, never, never>
26+
//
27+
const result = Effect.transposeOption(maybe)
28+
29+
console.log(Effect.runSync(result))
30+
// Output: { _id: 'Option', _tag: 'Some', value: 42 }
31+
```

packages/effect/dtslint/Effect.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,3 +1350,13 @@ hole<
13501350
}>
13511351
>
13521352
>()
1353+
1354+
// -------------------------------------------------------------------------------------
1355+
// transposeOption
1356+
// -------------------------------------------------------------------------------------
1357+
1358+
// $ExpectType Effect<Option<never>, never, never>
1359+
Effect.transposeOption(Option.none())
1360+
1361+
// $ExpectType Effect<Option<string>, "err-1", "dep-1">
1362+
Effect.transposeOption(Option.some(string))

packages/effect/src/Effect.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import * as defaultServices from "./internal/defaultServices.js"
3434
import * as circular from "./internal/effect/circular.js"
3535
import * as fiberRuntime from "./internal/fiberRuntime.js"
3636
import * as layer from "./internal/layer.js"
37+
import * as option_ from "./internal/option.js"
3738
import * as query from "./internal/query.js"
3839
import * as runtime_ from "./internal/runtime.js"
3940
import * as schedule_ from "./internal/schedule.js"
@@ -12811,7 +12812,7 @@ export const withParentSpan: {
1281112812
* ```
1281212813
*
1281312814
* @since 2.0.0
12814-
* @category Optional Wrapping
12815+
* @category Optional Wrapping & Unwrapping
1281512816
*/
1281612817
export const fromNullable: <A>(value: A) => Effect<NonNullable<A>, Cause.NoSuchElementException> = effect.fromNullable
1281712818

@@ -12866,12 +12867,47 @@ export const fromNullable: <A>(value: A) => Effect<NonNullable<A>, Cause.NoSuchE
1286612867
* ```
1286712868
*
1286812869
* @since 2.0.0
12869-
* @category Optional Wrapping
12870+
* @category Optional Wrapping & Unwrapping
1287012871
*/
1287112872
export const optionFromOptional: <A, E, R>(
1287212873
self: Effect<A, E, R>
1287312874
) => Effect<Option.Option<A>, Exclude<E, Cause.NoSuchElementException>, R> = effect.optionFromOptional
1287412875

12876+
/**
12877+
* Converts an `Option` of an `Effect` into an `Effect` of an `Option`.
12878+
*
12879+
* **Details**
12880+
*
12881+
* This function transforms an `Option<Effect<A, E, R>>` into an
12882+
* `Effect<Option<A>, E, R>`. If the `Option` is `None`, the resulting `Effect`
12883+
* will immediately succeed with a `None` value. If the `Option` is `Some`, the
12884+
* inner `Effect` will be executed, and its result wrapped in a `Some`.
12885+
*
12886+
* @example
12887+
* ```ts
12888+
* import { Effect, Option } from "effect"
12889+
*
12890+
* // ┌─── Option<Effect<number, never, never>>
12891+
* // ▼
12892+
* const maybe = Option.some(Effect.succeed(42))
12893+
*
12894+
* // ┌─── Effect<Option<number>, never, never>
12895+
* // ▼
12896+
* const result = Effect.transposeOption(maybe)
12897+
*
12898+
* console.log(Effect.runSync(result))
12899+
* // Output: { _id: 'Option', _tag: 'Some', value: 42 }
12900+
* ```
12901+
*
12902+
* @since 3.13.0
12903+
* @category Optional Wrapping & Unwrapping
12904+
*/
12905+
export const transposeOption = <A = never, E = never, R = never>(
12906+
self: Option.Option<Effect<A, E, R>>
12907+
): Effect<Option.Option<A>, E, R> => {
12908+
return option_.isNone(self) ? succeedNone : map(self.value, option_.some)
12909+
}
12910+
1287512911
/**
1287612912
* @since 2.0.0
1287712913
* @category Models
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as Effect from "effect/Effect"
2+
import * as Option from "effect/Option"
3+
import * as it from "effect/test/utils/extend"
4+
import { assert, describe } from "vitest"
5+
6+
describe("Effect", () => {
7+
describe("transposeOption", () => {
8+
it.effect("None", () =>
9+
Effect.gen(function*() {
10+
const result = yield* Effect.transposeOption(Option.none())
11+
assert.ok(Option.isNone(result))
12+
}))
13+
14+
it.effect("Some", () =>
15+
Effect.gen(function*() {
16+
const result = yield* Effect.transposeOption(Option.some(Effect.succeed(42)))
17+
assert.deepStrictEqual(result, Option.some(42))
18+
}))
19+
})
20+
})

0 commit comments

Comments
 (0)