Skip to content

Commit

Permalink
add Rx.context api for building layers
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed Dec 6, 2023
1 parent 1d6d8a3 commit a15e989
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 72 deletions.
5 changes: 5 additions & 0 deletions .changeset/brown-deers-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect-rx/rx": minor
---

add Rx.context api for building layers
17 changes: 12 additions & 5 deletions docs/rx/Rx.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ Added in v1.0.0
- [withFallback](#withfallback)
- [withLabel](#withlabel)
- [constructors](#constructors)
- [context](#context)
- [family](#family)
- [fn](#fn)
- [fnSync](#fnsync)
- [make](#make)
- [pull](#pull)
- [readable](#readable)
- [writable](#writable)
- [context](#context)
- [context](#context-1)
- [Context (interface)](#context-interface)
- [WriteContext (interface)](#writecontext-interface)
- [models](#models)
Expand Down Expand Up @@ -204,6 +205,16 @@ Added in v1.0.0
# constructors
## context
**Signature**
```ts
export declare const context: () => <E, R>(layer: Layer.Layer<never, E, R>) => RxRuntime<E, R>
```
Added in v1.0.0
## family
**Signature**
Expand Down Expand Up @@ -268,8 +279,6 @@ export declare const make: {
create: Rx.Read<Stream.Stream<never, E, A>>,
options?: { readonly initialValue?: A | undefined } | undefined
): Rx<Result.Result<E, A>>
<E, A>(layer: Layer.Layer<never, E, A>): RxRuntime<E, A>
<E, A>(create: Rx.Read<Layer.Layer<never, E, A>>): RxRuntime<E, A>
<A>(create: Rx.Read<A>): Rx<A>
<A>(initialValue: A): Writable<A, A>
}
Expand Down Expand Up @@ -590,8 +599,6 @@ export interface RxRuntime<ER, R> extends Rx<Result.Result<ER, Runtime.Runtime<R
readonly initialValue?: A
}
): Rx<Result.Result<E | ER, A>>
<E, A>(layer: Layer.Layer<R, E, A>): RxRuntime<E | ER, A | R>
<E, A>(create: Rx.Read<Layer.Layer<R, E, A>>): RxRuntime<E | ER, A | R>
}

readonly fn: {
Expand Down
101 changes: 38 additions & 63 deletions packages/rx/src/Rx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ const RxProto = {
}
return makeEffect(
get,
makeStreamPullEffect(get, arg, options, runtimeResult.value[0]),
makeStreamPullEffect(get, arg, options, runtimeResult.value),
Result.initial(true),
runtimeResult.value[0]
runtimeResult.value
)
})
return makeStreamPull(pullRx as any, options)
Expand Down Expand Up @@ -339,8 +339,6 @@ export const make: {
<E, A>(create: Rx.Read<Stream.Stream<never, E, A>>, options?: {
readonly initialValue?: A
}): Rx<Result.Result<E, A>>
<E, A>(layer: Layer.Layer<never, E, A>): RxRuntime<E, A>
<E, A>(create: Rx.Read<Layer.Layer<never, E, A>>): RxRuntime<E, A>
<A>(create: Rx.Read<A>): Rx<A>
<A>(initialValue: A): Writable<A, A>
} = (arg: any, options?: { readonly initialValue?: unknown }) => {
Expand All @@ -358,63 +356,49 @@ export const make: {
const makeRead: {
<E, A>(effect: Effect.Effect<Scope.Scope, E, A>, options?: {
readonly initialValue?: A
}): (get: Context, runtime?: RuntimeTuple) => Result.Result<E, A>
}): (get: Context, runtime?: Runtime.Runtime<any>) => Result.Result<E, A>
<E, A>(create: Rx.Read<Effect.Effect<Scope.Scope, E, A>>, options?: {
readonly initialValue?: A
}): (get: Context, runtime?: RuntimeTuple) => Result.Result<E, A>
}): (get: Context, runtime?: Runtime.Runtime<any>) => Result.Result<E, A>
<E, A>(stream: Stream.Stream<never, E, A>, options?: {
readonly initialValue?: A
}): (get: Context, runtime?: RuntimeTuple) => Result.Result<E, A>
}): (get: Context, runtime?: Runtime.Runtime<any>) => Result.Result<E, A>
<E, A>(create: Rx.Read<Stream.Stream<never, E, A>>, options?: {
readonly initialValue?: A
}): (get: Context, runtime?: RuntimeTuple) => Result.Result<E, A>
<E, A>(
layer: Layer.Layer<never, E, A>
): (get: Context, runtime?: RuntimeTuple) => Result.Result<E, RuntimeTuple>
<E, A>(
create: Rx.Read<Layer.Layer<never, E, A>>
): (get: Context, runtime?: RuntimeTuple) => Result.Result<E, RuntimeTuple>
<A>(create: Rx.Read<A>): (get: Context, runtime?: RuntimeTuple) => A
}): (get: Context, runtime?: Runtime.Runtime<any>) => Result.Result<E, A>
<A>(create: Rx.Read<A>): (get: Context, runtime?: Runtime.Runtime<any>) => A
<A>(initialValue: A): Writable<A, A>
} = <E, A>(
arg:
| Effect.Effect<Scope.Scope, E, A>
| Rx.Read<Effect.Effect<Scope.Scope, E, A>>
| Stream.Stream<never, E, A>
| Rx.Read<Stream.Stream<never, E, A>>
| Layer.Layer<never, E, A>
| Rx.Read<Layer.Layer<never, E, A>>
| Rx.Read<A>
| A,
options?: { readonly initialValue?: unknown }
) => {
if (typeof arg === "function") {
const create = arg as Rx.Read<any>
return function(get: Context, providedRuntime?: RuntimeTuple) {
return function(get: Context, providedRuntime?: Runtime.Runtime<any>) {
const value = create(get)
if (typeof value === "object" && value !== null) {
if (Effect.EffectTypeId in value) {
return effect(get, value, options, providedRuntime ? providedRuntime[0] : undefined)
return effect(get, value, options, providedRuntime)
} else if (Stream.StreamTypeId in value) {
return stream(get, value, options, providedRuntime ? providedRuntime[0] : undefined)
} else if (Layer.LayerTypeId in value) {
return runtime(get, value, providedRuntime)
return stream(get, value, options, providedRuntime)
}
}
return value
}
} else if (typeof arg === "object" && arg !== null) {
if (Effect.EffectTypeId in arg) {
return function(get: Context, providedRuntime?: RuntimeTuple) {
return effect(get, arg, options, providedRuntime ? providedRuntime[0] : undefined)
return function(get: Context, providedRuntime?: Runtime.Runtime<any>) {
return effect(get, arg, options, providedRuntime)
}
} else if (Stream.StreamTypeId in arg) {
return function(get: Context, providedRuntime?: RuntimeTuple) {
return stream(get, arg, options, providedRuntime ? providedRuntime[0] : undefined)
}
} else if (Layer.LayerTypeId in arg) {
return function(get: Context, providedRuntime?: RuntimeTuple) {
return runtime(get, arg, providedRuntime)
return function(get: Context, providedRuntime?: Runtime.Runtime<any>) {
return stream(get, arg, options, providedRuntime)
}
}
}
Expand Down Expand Up @@ -473,16 +457,14 @@ function makeEffect<E, A>(
}

// -----------------------------------------------------------------------------
// constructors - layer
// context
// -----------------------------------------------------------------------------

type RuntimeTuple<R = any> = readonly [Runtime.Runtime<R>, Layer.MemoMap]

/**
* @since 1.0.0
* @category models
*/
export interface RxRuntime<ER, R> extends Rx<Result.Result<ER, RuntimeTuple<R>>> {
export interface RxRuntime<ER, R> extends Rx<Result.Result<ER, Runtime.Runtime<R>>> {
readonly rx: {
<E, A>(effect: Effect.Effect<Scope.Scope | R, E, A>, options?: {
readonly initialValue?: A
Expand All @@ -496,8 +478,6 @@ export interface RxRuntime<ER, R> extends Rx<Result.Result<ER, RuntimeTuple<R>>>
<E, A>(create: Rx.Read<Stream.Stream<never | R, E, A>>, options?: {
readonly initialValue?: A
}): Rx<Result.Result<E | ER, A>>
<E, A>(layer: Layer.Layer<R, E, A>): RxRuntime<E | ER, A | R>
<E, A>(create: Rx.Read<Layer.Layer<R, E, A>>): RxRuntime<E | ER, A | R>
}

readonly fn: {
Expand All @@ -515,30 +495,25 @@ export interface RxRuntime<ER, R> extends Rx<Result.Result<ER, RuntimeTuple<R>>>
}) => Writable<PullResult<E | ER, A>, void>
}

const runtime = <E, A>(
get: Context,
layer: Layer.Layer<never, E, A>,
runtime?: RuntimeTuple
): Result.Result<E, RuntimeTuple<A>> => {
if (runtime) {
const buildEffect = pipe(
Effect.flatMap(Effect.scope, (scope) => {
return Layer.buildWithMemoMap(layer, runtime[1], scope)
}),
Effect.flatMap((context) => Effect.provide(Effect.runtime<A>(), context)),
Effect.map((rt) => [rt, runtime[1]] as const)
)

return effect(get, buildEffect, undefined, runtime[0])
}

const buildEffect = Effect.flatMap(Layer.makeMemoMap, (memoMap) =>
pipe(
Effect.flatMap(Effect.scope, (scope) => Layer.buildWithMemoMap(layer, memoMap, scope)),
Effect.flatMap((context) => Effect.provide(Effect.runtime<A>(), context)),
Effect.map((rt) => [rt, memoMap] as const)
))
return effect(get, buildEffect)
/**
* @since 1.0.0
* @category constructors
*/
export const context: () => <E, R>(layer: Layer.Layer<never, E, R>) => RxRuntime<E, R> = () => {
const memoMapRx = make(Layer.makeMemoMap)
return <E, R>(layer: Layer.Layer<never, E, R>): RxRuntime<E, R> =>
readable(function(get) {
const memoMapResult = get(memoMapRx)
if (memoMapResult._tag !== "Success") {
return Result.initial(true)
}
const memoMap = memoMapResult.value
const build = Effect.flatMap(
Effect.flatMap(Effect.scope, (scope) => Layer.buildWithMemoMap(layer, memoMap, scope)),
(context) => Effect.provide(Effect.runtime<R>(), context)
)
return effect(get, build)
}) as any
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -662,16 +637,16 @@ function makeResultFn<Arg, E, A>(
? Result.success<E, A>(options.initialValue)
: Result.initial<E, A>()

function read(get: Context, runtime?: RuntimeTuple): Result.Result<E | NoSuchElementException, A> {
function read(get: Context, runtime?: Runtime.Runtime<any>): Result.Result<E | NoSuchElementException, A> {
const [counter, arg] = get(argRx)
if (counter === 0) {
return initialValue
}
const value = f(arg, get)
if (Effect.EffectTypeId in value) {
return makeEffect(get, value, initialValue, runtime ? runtime[0] : undefined)
return makeEffect(get, value, initialValue, runtime)
}
return makeStream(get, value, initialValue, runtime ? runtime[0] : undefined)
return makeStream(get, value, initialValue, runtime)
}
function write(ctx: WriteContext<Result.Result<E | NoSuchElementException, A>>, arg: Arg) {
ctx.set(argRx, [ctx.get(argRx)[0] + 1, arg])
Expand Down
14 changes: 10 additions & 4 deletions packages/rx/test/Rx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,8 @@ const CounterLive = Layer.effect(
})
})
})
).pipe(
Layer.provide(BuildCounterLive)
)

interface Multiplier {
Expand All @@ -765,9 +767,13 @@ const MultiplierLive = Layer.effect(
times: (n) => Effect.map(counter.get, (_) => _ * n)
})
})
).pipe(
Layer.provideMerge(CounterLive)
)

const buildCounterRuntime = Rx.make(BuildCounterLive)
const counterRuntime = buildCounterRuntime.rx(CounterLive)
const multiplierRuntime = counterRuntime.rx(MultiplierLive.pipe(Layer.provide(CounterLive)))
const fiberRefRuntime = counterRuntime.rx(Layer.setRequestCaching(true))
const rxContext = Rx.context()

const buildCounterRuntime = rxContext(BuildCounterLive)
const counterRuntime = rxContext(CounterLive)
const multiplierRuntime = rxContext(MultiplierLive)
const fiberRefRuntime = rxContext(Layer.setRequestCaching(true))

0 comments on commit a15e989

Please sign in to comment.