This commit is contained in:
@@ -240,21 +240,21 @@ const nameLens = lens.pipe(
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Subscribable
|
### View
|
||||||
|
|
||||||
Effect 4 no longer provides `Subscribable` and `Readable`. This library provides its own `Subscribable` interface, which you can use as a constraint to allow some parts of your app to only read and subscribe to the Lenses you provide them:
|
Effect 4 no longer provides its former `Subscribable` and `Readable` abstractions. This library provides a `View` interface, which you can use as a constraint to allow some parts of your app to only read and subscribe to the Lenses you provide them:
|
||||||
```typescript
|
```typescript
|
||||||
const ref = yield* SubscriptionRef.make<{
|
const ref = yield* SubscriptionRef.make<{
|
||||||
readonly users: readonly User[]
|
readonly users: readonly User[]
|
||||||
}>({ users: [...] })
|
}>({ users: [...] })
|
||||||
|
|
||||||
const someFunctionThatShouldOnlyHaveReadonlyAccessToTheState = (
|
const someFunctionThatShouldOnlyHaveReadonlyAccessToTheState = (
|
||||||
usersSub: Subscribable.Subscribable<readonly User[], never, never>
|
usersView: View.View<readonly User[], never, never>
|
||||||
) => Effect.gen(function*() {
|
) => Effect.gen(function*() {
|
||||||
// Do whatever
|
// Do whatever
|
||||||
const usersCountSub = Subscribable.map(usersSub, a => a.length)
|
const usersCountView = View.map(usersView, a => a.length)
|
||||||
const users = yield* usersSub.get
|
const users = yield* usersView.get
|
||||||
yield* Effect.forkScoped(Stream.runForEach(usersSub.changes, ...))
|
yield* Effect.forkScoped(Stream.runForEach(usersView.changes, ...))
|
||||||
})
|
})
|
||||||
|
|
||||||
const lens = ref.pipe(
|
const lens = ref.pipe(
|
||||||
@@ -265,16 +265,16 @@ yield* someFunctionThatShouldOnlyHaveReadonlyAccessToTheState(lens)
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Focusing
|
#### Focusing
|
||||||
This library provides a `Subscribable` module with value and error transforms, recovery (`catch`, `catchCause`, `orElse`, `orElseSucceed`, and `retry`), and transforms to narrow the focus of `Subscribable`'s, same as Lenses:
|
This library provides a `View` module with value and error transforms, recovery (`catch`, `catchCause`, `orElse`, `orElseSucceed`, and `retry`), and transforms to narrow the focus of `View`'s, same as Lenses:
|
||||||
```typescript
|
```typescript
|
||||||
import { Subscribable } from "effect-lens"
|
import { View } from "effect-lens"
|
||||||
|
|
||||||
declare const sub: Subscribable.Subscribable<readonly { name: string }[], never, never>
|
declare const view: View.View<readonly { name: string }[], never, never>
|
||||||
|
|
||||||
// \/ Subscribable.Subscribable<string, NoSuchElementError, never>
|
// \/ View.View<string, NoSuchElementError, never>
|
||||||
const nameSub = sub.pipe(
|
const nameView = view.pipe(
|
||||||
Subscribable.focusArrayAt(1),
|
View.focusArrayAt(1),
|
||||||
Subscribable.focusObjectOn("name"),
|
View.focusObjectOn("name"),
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
"types": "./dist/Lens.d.ts",
|
"types": "./dist/Lens.d.ts",
|
||||||
"default": "./dist/Lens.js"
|
"default": "./dist/Lens.js"
|
||||||
},
|
},
|
||||||
"./Subscribable": {
|
"./View": {
|
||||||
"types": "./dist/Subscribable.d.ts",
|
"types": "./dist/View.d.ts",
|
||||||
"default": "./dist/Subscribable.js"
|
"default": "./dist/View.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
SubscriptionRef,
|
SubscriptionRef,
|
||||||
SynchronizedRef,
|
SynchronizedRef,
|
||||||
} from "effect"
|
} from "effect"
|
||||||
import * as Subscribable from "./Subscribable.js"
|
import * as View from "./View.js"
|
||||||
|
|
||||||
|
|
||||||
export const LensTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/Lens")
|
export const LensTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/Lens")
|
||||||
@@ -30,7 +30,7 @@ export type LensTypeId = typeof LensTypeId
|
|||||||
* 3. a `modify` effect that can transform the current value.
|
* 3. a `modify` effect that can transform the current value.
|
||||||
*/
|
*/
|
||||||
export interface Lens<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
export interface Lens<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
||||||
extends Subscribable.Subscribable<A, ER, RR> {
|
extends View.View<A, ER, RR> {
|
||||||
readonly [LensTypeId]: LensTypeId
|
readonly [LensTypeId]: LensTypeId
|
||||||
|
|
||||||
readonly modifyEffect: <B, E1 = never, R1 = never>(
|
readonly modifyEffect: <B, E1 = never, R1 = never>(
|
||||||
@@ -66,7 +66,7 @@ export declare namespace LensImpl {
|
|||||||
|
|
||||||
export abstract class LensImpl<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
export abstract class LensImpl<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
||||||
extends Pipeable.Class implements Lens<A, ER, EW, RR, RW> {
|
extends Pipeable.Class implements Lens<A, ER, EW, RR, RW> {
|
||||||
readonly [Subscribable.SubscribableTypeId]: Subscribable.SubscribableTypeId = Subscribable.SubscribableTypeId
|
readonly [View.ViewTypeId]: View.ViewTypeId = View.ViewTypeId
|
||||||
readonly [LensTypeId]: LensTypeId = LensTypeId
|
readonly [LensTypeId]: LensTypeId = LensTypeId
|
||||||
readonly [LensImplTypeId]: LensImplTypeId = LensImplTypeId
|
readonly [LensImplTypeId]: LensImplTypeId = LensImplTypeId
|
||||||
|
|
||||||
@@ -120,9 +120,9 @@ export const asLensImpl = <A, ER, EW, RR, RW>(
|
|||||||
return lens as LensImpl<A, ER, EW, RR, RW>
|
return lens as LensImpl<A, ER, EW, RR, RW>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const asSubscribable = <A, ER, EW, RR, RW>(
|
export const asView = <A, ER, EW, RR, RW>(
|
||||||
lens: Lens<A, ER, EW, RR, RW>
|
lens: Lens<A, ER, EW, RR, RW>
|
||||||
): Subscribable.Subscribable<A, ER, RR> => lens
|
): View.View<A, ER, RR> => lens
|
||||||
|
|
||||||
|
|
||||||
export declare namespace LensLazyImpl {
|
export declare namespace LensLazyImpl {
|
||||||
|
|||||||
@@ -1,246 +0,0 @@
|
|||||||
import { Array, type Cause, Chunk, Effect, Function, Iterable, Option, Pipeable, Predicate, type Result, type Schedule, Stream } from "effect"
|
|
||||||
|
|
||||||
|
|
||||||
export const SubscribableTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/Subscribable")
|
|
||||||
export type SubscribableTypeId = typeof SubscribableTypeId
|
|
||||||
|
|
||||||
export interface Subscribable<in out A, in out E = never, in out R = never> extends Pipeable.Pipeable {
|
|
||||||
readonly [SubscribableTypeId]: SubscribableTypeId
|
|
||||||
readonly get: Effect.Effect<A, E, R>
|
|
||||||
readonly changes: Stream.Stream<A, E, R>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isSubscribable = (u: unknown): u is Subscribable<unknown, unknown, unknown> => Predicate.hasProperty(u, SubscribableTypeId)
|
|
||||||
|
|
||||||
|
|
||||||
export const SubscribableImplTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/SubscribableImpl")
|
|
||||||
export type SubscribableImplTypeId = typeof SubscribableImplTypeId
|
|
||||||
|
|
||||||
export declare namespace SubscribableImpl {
|
|
||||||
export interface Source<in out A, in out E = never, in out R = never> {
|
|
||||||
readonly get: Effect.Effect<A, E, R>
|
|
||||||
readonly changes: Stream.Stream<A, E, R>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SubscribableImpl<in out A, in out E = never, in out R = never>
|
|
||||||
extends Pipeable.Class implements Subscribable<A, E, R> {
|
|
||||||
readonly [SubscribableTypeId]: SubscribableTypeId = SubscribableTypeId
|
|
||||||
readonly [SubscribableImplTypeId]: SubscribableImplTypeId = SubscribableImplTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly source: SubscribableImpl.Source<A, E, R>,
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
get get() { return this.source.get }
|
|
||||||
get changes() { return this.source.changes }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isSubscribableImpl = (u: unknown): u is SubscribableImpl<unknown, unknown, unknown> => Predicate.hasProperty(u, SubscribableImplTypeId)
|
|
||||||
|
|
||||||
export const asSubscribableImpl = <A, E, R>(
|
|
||||||
subscribable: Subscribable<A, E, R>
|
|
||||||
): SubscribableImpl<A, E, R> => {
|
|
||||||
if (!isSubscribableImpl(subscribable))
|
|
||||||
throw new Error("Not a 'SubscribableImpl'")
|
|
||||||
return subscribable as SubscribableImpl<A, E, R>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make = <A, E, R>(
|
|
||||||
source: SubscribableImpl.Source<A, E, R>
|
|
||||||
): Subscribable<A, E, R> => new SubscribableImpl(source)
|
|
||||||
|
|
||||||
export const unwrap = <A, E, R, E1, R1>(
|
|
||||||
effect: Effect.Effect<Subscribable<A, E, R>, E1, R1>,
|
|
||||||
): Subscribable<A, E | E1, R | R1> => make({
|
|
||||||
get: Effect.flatMap(effect, self => self.get),
|
|
||||||
changes: Stream.unwrap(Effect.map(effect, self => self.changes)),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
export const map: {
|
|
||||||
<A, B>(f: (a: NoInfer<A>) => B): <E, R>(self: Subscribable<A, E, R>) => Subscribable<B, E, R>
|
|
||||||
<A, E, R, B>(self: Subscribable<A, E, R>, f: (a: NoInfer<A>) => B): Subscribable<B, E, R>
|
|
||||||
} = Function.dual(2, <A, E, R, B>(self: Subscribable<A, E, R>, f: (a: NoInfer<A>) => B) => make({
|
|
||||||
get get() { return Effect.map(self.get, f) },
|
|
||||||
get changes() { return Stream.map(self.changes, f) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const mapEffect: {
|
|
||||||
<A, B, E2, R2>(f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>): <E, R>(self: Subscribable<A, E, R>) => Subscribable<B, E | E2, R | R2>
|
|
||||||
<A, E, R, B, E2, R2>(self: Subscribable<A, E, R>, f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>): Subscribable<B, E | E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.flatMap(self.get, f) },
|
|
||||||
get changes() { return Stream.mapEffect(self.changes, f) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Maps over an `Option` value in the `Subscribable`. */
|
|
||||||
export const mapOption: {
|
|
||||||
<A, B>(f: (a: A) => B): <E, R>(self: Subscribable<Option.Option<A>, E, R>) => Subscribable<Option.Option<B>, E, R>
|
|
||||||
<A, B, E, R>(self: Subscribable<Option.Option<A>, E, R>, f: (a: A) => B): Subscribable<Option.Option<B>, E, R>
|
|
||||||
} = Function.dual(2, <A, B, E, R>(self: Subscribable<Option.Option<A>, E, R>, f: (a: A) => B) =>
|
|
||||||
map(self, Option.map(f)),
|
|
||||||
)
|
|
||||||
|
|
||||||
/** Maps over an `Option` value in the `Subscribable` with an Effect. */
|
|
||||||
export const mapOptionEffect: {
|
|
||||||
<A, B, E2, R2>(f: (a: A) => Effect.Effect<B, E2, R2>): <E, R>(self: Subscribable<Option.Option<A>, E, R>) => Subscribable<Option.Option<B>, E | E2, R | R2>
|
|
||||||
<A, B, E, R, E2, R2>(self: Subscribable<Option.Option<A>, E, R>, f: (a: A) => Effect.Effect<B, E2, R2>): Subscribable<Option.Option<B>, E | E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, B, E, R, E2, R2>(
|
|
||||||
self: Subscribable<Option.Option<A>, E, R>,
|
|
||||||
f: (a: A) => Effect.Effect<B, E2, R2>,
|
|
||||||
) => mapEffect(self, Option.match({
|
|
||||||
onSome: a => Effect.map(f(a), Option.some),
|
|
||||||
onNone: () => Effect.succeed(Option.none()),
|
|
||||||
})))
|
|
||||||
|
|
||||||
|
|
||||||
/** Maps errors from both the current value and the stream of changes. */
|
|
||||||
export const mapError: {
|
|
||||||
<E, E2>(f: (error: NoInfer<E>) => E2): <A, R>(self: Subscribable<A, E, R>) => Subscribable<A, E2, R>
|
|
||||||
<A, E, R, E2>(self: Subscribable<A, E, R>, f: (error: NoInfer<E>) => E2): Subscribable<A, E2, R>
|
|
||||||
} = Function.dual(2, <A, E, R, E2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
f: (error: NoInfer<E>) => E2,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.mapError(self.get, f) },
|
|
||||||
get changes() { return Stream.mapError(self.changes, f) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Runs an effect when either the current value or the stream of changes fails. */
|
|
||||||
export const tapError: {
|
|
||||||
<E, B, E2, R2>(f: (error: NoInfer<E>) => Effect.Effect<B, E2, R2>): <A, R>(self: Subscribable<A, E, R>) => Subscribable<A, E | E2, R | R2>
|
|
||||||
<A, E, R, B, E2, R2>(self: Subscribable<A, E, R>, f: (error: NoInfer<E>) => Effect.Effect<B, E2, R2>): Subscribable<A, E | E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
f: (error: NoInfer<E>) => Effect.Effect<B, E2, R2>,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.tapError(self.get, f) },
|
|
||||||
get changes() { return Stream.tapError(self.changes, f) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
const catch_: {
|
|
||||||
<E, B, E2, R2>(f: (error: NoInfer<E>) => Subscribable<B, E2, R2>): <A, R>(self: Subscribable<A, E, R>) => Subscribable<A | B, E2, R | R2>
|
|
||||||
<A, E, R, B, E2, R2>(self: Subscribable<A, E, R>, f: (error: NoInfer<E>) => Subscribable<B, E2, R2>): Subscribable<A | B, E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
f: (error: NoInfer<E>) => Subscribable<B, E2, R2>,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.catch(self.get, error => f(error).get) },
|
|
||||||
get changes() { return Stream.catch(self.changes, error => f(error).changes) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Recovers from typed errors with another `Subscribable`. */
|
|
||||||
export { catch_ as catch }
|
|
||||||
|
|
||||||
/** Recovers from all failure causes with another `Subscribable`. */
|
|
||||||
export const catchCause: {
|
|
||||||
<E, B, E2, R2>(f: (cause: Cause.Cause<NoInfer<E>>) => Subscribable<B, E2, R2>): <A, R>(self: Subscribable<A, E, R>) => Subscribable<A | B, E2, R | R2>
|
|
||||||
<A, E, R, B, E2, R2>(self: Subscribable<A, E, R>, f: (cause: Cause.Cause<NoInfer<E>>) => Subscribable<B, E2, R2>): Subscribable<A | B, E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
f: (cause: Cause.Cause<NoInfer<E>>) => Subscribable<B, E2, R2>,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.catchCause(self.get, cause => f(cause).get) },
|
|
||||||
get changes() { return Stream.catchCause(self.changes, cause => f(cause).changes) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Runs an effect when either channel fails, exposing the complete failure cause. */
|
|
||||||
export const tapCause: {
|
|
||||||
<E, B, E2, R2>(f: (cause: Cause.Cause<NoInfer<E>>) => Effect.Effect<B, E2, R2>): <A, R>(self: Subscribable<A, E, R>) => Subscribable<A, E | E2, R | R2>
|
|
||||||
<A, E, R, B, E2, R2>(self: Subscribable<A, E, R>, f: (cause: Cause.Cause<NoInfer<E>>) => Effect.Effect<B, E2, R2>): Subscribable<A, E | E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
f: (cause: Cause.Cause<NoInfer<E>>) => Effect.Effect<B, E2, R2>,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.tapCause(self.get, f) },
|
|
||||||
get changes() { return Stream.tapCause(self.changes, f) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Falls back to another `Subscribable` when either channel fails. */
|
|
||||||
export const orElse: {
|
|
||||||
<B, E2, R2>(that: () => Subscribable<B, E2, R2>): <A, E, R>(self: Subscribable<A, E, R>) => Subscribable<A | B, E2, R | R2>
|
|
||||||
<A, E, R, B, E2, R2>(self: Subscribable<A, E, R>, that: () => Subscribable<B, E2, R2>): Subscribable<A | B, E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
that: () => Subscribable<B, E2, R2>,
|
|
||||||
) => catch_(self, that))
|
|
||||||
|
|
||||||
/** Replaces typed errors from either channel with a lazily evaluated value. */
|
|
||||||
export const orElseSucceed: {
|
|
||||||
<B>(value: () => B): <A, E, R>(self: Subscribable<A, E, R>) => Subscribable<A | B, never, R>
|
|
||||||
<A, E, R, B>(self: Subscribable<A, E, R>, value: () => B): Subscribable<A | B, never, R>
|
|
||||||
} = Function.dual(2, <A, E, R, B>(self: Subscribable<A, E, R>, value: () => B) => make({
|
|
||||||
get get() { return Effect.orElseSucceed(self.get, value) },
|
|
||||||
get changes() { return Stream.orElseSucceed(self.changes, value) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Retries failures from both channels according to the supplied schedule. */
|
|
||||||
export const retry: {
|
|
||||||
<E, X, E2, R2>(policy: Schedule.Schedule<X, NoInfer<E>, E2, R2>): <A, R>(self: Subscribable<A, E, R>) => Subscribable<A, E | E2, R | R2>
|
|
||||||
<A, E, R, X, E2, R2>(self: Subscribable<A, E, R>, policy: Schedule.Schedule<X, NoInfer<E>, E2, R2>): Subscribable<A, E | E2, R | R2>
|
|
||||||
} = Function.dual(2, <A, E, R, X, E2, R2>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
policy: Schedule.Schedule<X, NoInfer<E>, E2, R2>,
|
|
||||||
) => make({
|
|
||||||
get get() { return Effect.retry(self.get, policy) },
|
|
||||||
get changes() { return Stream.retry(self.changes, policy) },
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** Converts typed failures from both channels into `Result` values. */
|
|
||||||
export const result = <A, E, R>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
): Subscribable<Result.Result<A, E>, never, R> => make({
|
|
||||||
get get() { return Effect.result(self.get) },
|
|
||||||
get changes() { return Stream.result(self.changes) },
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
/** Narrows the focus to a field of an object. */
|
|
||||||
export const focusObjectOn: {
|
|
||||||
<A extends object, K extends keyof A>(key: K): <E, R>(self: Subscribable<A, E, R>) => Subscribable<A[K], E, R>
|
|
||||||
<A extends object, K extends keyof A, E, R>(self: Subscribable<A, E, R>, key: K): Subscribable<A[K], E, R>
|
|
||||||
} = Function.dual(2, <A extends object, K extends keyof A, E, R>(self: Subscribable<A, E, R>, key: K) =>
|
|
||||||
map(self, a => a[key]),
|
|
||||||
)
|
|
||||||
|
|
||||||
/** Narrows the focus to an indexed element of an array. */
|
|
||||||
export const focusArrayAt: {
|
|
||||||
<A extends readonly any[]>(index: number): <E, R>(self: Subscribable<A, E, R>) => Subscribable<A[number], E | Cause.NoSuchElementError, R>
|
|
||||||
<A extends readonly any[], E, R>(self: Subscribable<A, E, R>, index: number): Subscribable<A[number], E | Cause.NoSuchElementError, R>
|
|
||||||
} = Function.dual(2, <A extends readonly any[], E, R>(self: Subscribable<A, E, R>, index: number) =>
|
|
||||||
mapEffect(self, a => Effect.fromOption(Array.get(a, index))),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const focusArrayLength = <A extends readonly any[], E, R>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
): Subscribable<number, E, R> => map(self, Array.length)
|
|
||||||
|
|
||||||
/** Narrows the focus to an indexed element of a readonly tuple. */
|
|
||||||
export const focusTupleAt: {
|
|
||||||
<T extends readonly [any, ...any[]], I extends number>(index: I): <E, R>(self: Subscribable<T, E, R>) => Subscribable<T[I], E, R>
|
|
||||||
<T extends readonly [any, ...any[]], I extends number, E, R>(self: Subscribable<T, E, R>, index: I): Subscribable<T[I], E, R>
|
|
||||||
} = Function.dual(2, <T extends readonly [any, ...any[]], I extends number, E, R>(self: Subscribable<T, E, R>, index: I) =>
|
|
||||||
map(self, Array.getUnsafe(index)),
|
|
||||||
)
|
|
||||||
|
|
||||||
/** Narrows the focus to an indexed element of `Chunk`. */
|
|
||||||
export const focusChunkAt: {
|
|
||||||
<A>(index: number): <E, R>(self: Subscribable<Chunk.Chunk<A>, E, R>) => Subscribable<A, E | Cause.NoSuchElementError, R>
|
|
||||||
<A, E, R>(self: Subscribable<Chunk.Chunk<A>, E, R>, index: number): Subscribable<A, E | Cause.NoSuchElementError, R>
|
|
||||||
} = Function.dual(2, <A, E, R>(self: Subscribable<Chunk.Chunk<A>, E, R>, index: number) =>
|
|
||||||
mapEffect(self, chunk => Effect.fromOption(Chunk.get(chunk, index))),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const focusChunkSize = <A, E, R>(
|
|
||||||
self: Subscribable<Chunk.Chunk<A>, E, R>,
|
|
||||||
): Subscribable<number, E, R> => map(self, Chunk.size)
|
|
||||||
|
|
||||||
export const focusIterableSize = <A extends Iterable<any>, E, R>(
|
|
||||||
self: Subscribable<A, E, R>,
|
|
||||||
): Subscribable<number, E, R> => map(self, Iterable.size)
|
|
||||||
+19
-19
@@ -1,16 +1,16 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { Chunk, Effect, Stream, SubscriptionRef } from "effect"
|
import { Chunk, Effect, Stream, SubscriptionRef } from "effect"
|
||||||
import * as Lens from "./Lens.js"
|
import * as Lens from "./Lens.js"
|
||||||
import * as Subscribable from "./Subscribable.js"
|
import * as View from "./View.js"
|
||||||
|
|
||||||
|
|
||||||
describe("Subscribable", () => {
|
describe("View", () => {
|
||||||
test("mapError transforms errors from get and changes", async () => {
|
test("mapError transforms errors from get and changes", async () => {
|
||||||
const source = Subscribable.make({
|
const source = View.make({
|
||||||
get: Effect.fail("get"),
|
get: Effect.fail("get"),
|
||||||
changes: Stream.fail("changes"),
|
changes: Stream.fail("changes"),
|
||||||
})
|
})
|
||||||
const mapped = source.pipe(Subscribable.mapError((error: string) => `mapped:${error}`))
|
const mapped = source.pipe(View.mapError((error: string) => `mapped:${error}`))
|
||||||
|
|
||||||
const result = await Effect.runPromise(Effect.gen(function*() {
|
const result = await Effect.runPromise(Effect.gen(function*() {
|
||||||
const getError = yield* Effect.flip(mapped.get)
|
const getError = yield* Effect.flip(mapped.get)
|
||||||
@@ -23,11 +23,11 @@ describe("Subscribable", () => {
|
|||||||
|
|
||||||
test("tapError observes errors from get and changes", async () => {
|
test("tapError observes errors from get and changes", async () => {
|
||||||
const observed: Array<string> = []
|
const observed: Array<string> = []
|
||||||
const source = Subscribable.make({
|
const source = View.make({
|
||||||
get: Effect.fail("get"),
|
get: Effect.fail("get"),
|
||||||
changes: Stream.fail("changes"),
|
changes: Stream.fail("changes"),
|
||||||
})
|
})
|
||||||
const tapped = Subscribable.tapError(source, error => Effect.sync(() => observed.push(error)))
|
const tapped = View.tapError(source, error => Effect.sync(() => observed.push(error)))
|
||||||
|
|
||||||
await Effect.runPromise(Effect.gen(function*() {
|
await Effect.runPromise(Effect.gen(function*() {
|
||||||
yield* Effect.flip(tapped.get)
|
yield* Effect.flip(tapped.get)
|
||||||
@@ -38,11 +38,11 @@ describe("Subscribable", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test("catch recovers get and changes with the corresponding fallback channel", async () => {
|
test("catch recovers get and changes with the corresponding fallback channel", async () => {
|
||||||
const source = Subscribable.make({
|
const source = View.make({
|
||||||
get: Effect.fail("get"),
|
get: Effect.fail("get"),
|
||||||
changes: Stream.fail("changes"),
|
changes: Stream.fail("changes"),
|
||||||
})
|
})
|
||||||
const recovered = source.pipe(Subscribable.catch(() => Subscribable.make({
|
const recovered = source.pipe(View.catch(() => View.make({
|
||||||
get: Effect.succeed("fallback-get"),
|
get: Effect.succeed("fallback-get"),
|
||||||
changes: Stream.succeed("fallback-changes"),
|
changes: Stream.succeed("fallback-changes"),
|
||||||
})))
|
})))
|
||||||
@@ -57,11 +57,11 @@ describe("Subscribable", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test("orElseSucceed recovers errors from get and changes", async () => {
|
test("orElseSucceed recovers errors from get and changes", async () => {
|
||||||
const source = Subscribable.make({
|
const source = View.make({
|
||||||
get: Effect.fail("get"),
|
get: Effect.fail("get"),
|
||||||
changes: Stream.fail("changes"),
|
changes: Stream.fail("changes"),
|
||||||
})
|
})
|
||||||
const recovered = Subscribable.orElseSucceed(source, () => "fallback")
|
const recovered = View.orElseSucceed(source, () => "fallback")
|
||||||
|
|
||||||
const result = await Effect.runPromise(Effect.gen(function*() {
|
const result = await Effect.runPromise(Effect.gen(function*() {
|
||||||
const current = yield* recovered.get
|
const current = yield* recovered.get
|
||||||
@@ -77,12 +77,12 @@ describe("Subscribable", () => {
|
|||||||
Effect.flatMap(
|
Effect.flatMap(
|
||||||
SubscriptionRef.make([1, 2, 3]),
|
SubscriptionRef.make([1, 2, 3]),
|
||||||
parent => {
|
parent => {
|
||||||
const sizeSub = Subscribable.focusArrayLength(Lens.fromSubscriptionRef(parent))
|
const sizeView = View.focusArrayLength(Lens.fromSubscriptionRef(parent))
|
||||||
return Effect.flatMap(
|
return Effect.flatMap(
|
||||||
sizeSub.get,
|
sizeView.get,
|
||||||
initial => Effect.flatMap(
|
initial => Effect.flatMap(
|
||||||
SubscriptionRef.set(parent, [1, 2, 3, 4, 5]),
|
SubscriptionRef.set(parent, [1, 2, 3, 4, 5]),
|
||||||
() => Effect.map(sizeSub.get, next => [initial, next] as const),
|
() => Effect.map(sizeView.get, next => [initial, next] as const),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -97,12 +97,12 @@ describe("Subscribable", () => {
|
|||||||
Effect.flatMap(
|
Effect.flatMap(
|
||||||
SubscriptionRef.make(Chunk.make(1, 2) as Chunk.Chunk<number>),
|
SubscriptionRef.make(Chunk.make(1, 2) as Chunk.Chunk<number>),
|
||||||
parent => {
|
parent => {
|
||||||
const sizeSub = Subscribable.focusChunkSize(Lens.fromSubscriptionRef(parent))
|
const sizeView = View.focusChunkSize(Lens.fromSubscriptionRef(parent))
|
||||||
return Effect.flatMap(
|
return Effect.flatMap(
|
||||||
sizeSub.get,
|
sizeView.get,
|
||||||
initial => Effect.flatMap(
|
initial => Effect.flatMap(
|
||||||
SubscriptionRef.set(parent, Chunk.make(1, 2, 3, 4)),
|
SubscriptionRef.set(parent, Chunk.make(1, 2, 3, 4)),
|
||||||
() => Effect.map(sizeSub.get, next => [initial, next] as const),
|
() => Effect.map(sizeView.get, next => [initial, next] as const),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -117,12 +117,12 @@ describe("Subscribable", () => {
|
|||||||
Effect.flatMap(
|
Effect.flatMap(
|
||||||
SubscriptionRef.make([1, 2, 3]),
|
SubscriptionRef.make([1, 2, 3]),
|
||||||
parent => {
|
parent => {
|
||||||
const sizeSub = Subscribable.focusIterableSize(Lens.fromSubscriptionRef(parent))
|
const sizeView = View.focusIterableSize(Lens.fromSubscriptionRef(parent))
|
||||||
return Effect.flatMap(
|
return Effect.flatMap(
|
||||||
sizeSub.get,
|
sizeView.get,
|
||||||
initial => Effect.flatMap(
|
initial => Effect.flatMap(
|
||||||
SubscriptionRef.set(parent, [1, 2, 3, 4, 5]),
|
SubscriptionRef.set(parent, [1, 2, 3, 4, 5]),
|
||||||
() => Effect.map(sizeSub.get, next => [initial, next] as const),
|
() => Effect.map(sizeView.get, next => [initial, next] as const),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
import { Array, type Cause, Chunk, Effect, Function, Iterable, Option, Pipeable, Predicate, type Result, type Schedule, Stream } from "effect"
|
||||||
|
|
||||||
|
|
||||||
|
export const ViewTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/View")
|
||||||
|
export type ViewTypeId = typeof ViewTypeId
|
||||||
|
|
||||||
|
export interface View<in out A, in out E = never, in out R = never> extends Pipeable.Pipeable {
|
||||||
|
readonly [ViewTypeId]: ViewTypeId
|
||||||
|
readonly get: Effect.Effect<A, E, R>
|
||||||
|
readonly changes: Stream.Stream<A, E, R>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isView = (u: unknown): u is View<unknown, unknown, unknown> => Predicate.hasProperty(u, ViewTypeId)
|
||||||
|
|
||||||
|
|
||||||
|
export const ViewImplTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/ViewImpl")
|
||||||
|
export type ViewImplTypeId = typeof ViewImplTypeId
|
||||||
|
|
||||||
|
export declare namespace ViewImpl {
|
||||||
|
export interface Source<in out A, in out E = never, in out R = never> {
|
||||||
|
readonly get: Effect.Effect<A, E, R>
|
||||||
|
readonly changes: Stream.Stream<A, E, R>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewImpl<in out A, in out E = never, in out R = never>
|
||||||
|
extends Pipeable.Class implements View<A, E, R> {
|
||||||
|
readonly [ViewTypeId]: ViewTypeId = ViewTypeId
|
||||||
|
readonly [ViewImplTypeId]: ViewImplTypeId = ViewImplTypeId
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly source: ViewImpl.Source<A, E, R>,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
get get() { return this.source.get }
|
||||||
|
get changes() { return this.source.changes }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isViewImpl = (u: unknown): u is ViewImpl<unknown, unknown, unknown> => Predicate.hasProperty(u, ViewImplTypeId)
|
||||||
|
|
||||||
|
export const asViewImpl = <A, E, R>(
|
||||||
|
view: View<A, E, R>
|
||||||
|
): ViewImpl<A, E, R> => {
|
||||||
|
if (!isViewImpl(view))
|
||||||
|
throw new Error("Not a 'ViewImpl'")
|
||||||
|
return view as ViewImpl<A, E, R>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const make = <A, E, R>(
|
||||||
|
source: ViewImpl.Source<A, E, R>
|
||||||
|
): View<A, E, R> => new ViewImpl(source)
|
||||||
|
|
||||||
|
export const unwrap = <A, E, R, E1, R1>(
|
||||||
|
effect: Effect.Effect<View<A, E, R>, E1, R1>,
|
||||||
|
): View<A, E | E1, R | R1> => make({
|
||||||
|
get: Effect.flatMap(effect, self => self.get),
|
||||||
|
changes: Stream.unwrap(Effect.map(effect, self => self.changes)),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export const map: {
|
||||||
|
<A, B>(f: (a: NoInfer<A>) => B): <E, R>(self: View<A, E, R>) => View<B, E, R>
|
||||||
|
<A, E, R, B>(self: View<A, E, R>, f: (a: NoInfer<A>) => B): View<B, E, R>
|
||||||
|
} = Function.dual(2, <A, E, R, B>(self: View<A, E, R>, f: (a: NoInfer<A>) => B) => make({
|
||||||
|
get get() { return Effect.map(self.get, f) },
|
||||||
|
get changes() { return Stream.map(self.changes, f) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const mapEffect: {
|
||||||
|
<A, B, E2, R2>(f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>): <E, R>(self: View<A, E, R>) => View<B, E | E2, R | R2>
|
||||||
|
<A, E, R, B, E2, R2>(self: View<A, E, R>, f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>): View<B, E | E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.flatMap(self.get, f) },
|
||||||
|
get changes() { return Stream.mapEffect(self.changes, f) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Maps over an `Option` value in the `View`. */
|
||||||
|
export const mapOption: {
|
||||||
|
<A, B>(f: (a: A) => B): <E, R>(self: View<Option.Option<A>, E, R>) => View<Option.Option<B>, E, R>
|
||||||
|
<A, B, E, R>(self: View<Option.Option<A>, E, R>, f: (a: A) => B): View<Option.Option<B>, E, R>
|
||||||
|
} = Function.dual(2, <A, B, E, R>(self: View<Option.Option<A>, E, R>, f: (a: A) => B) =>
|
||||||
|
map(self, Option.map(f)),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Maps over an `Option` value in the `View` with an Effect. */
|
||||||
|
export const mapOptionEffect: {
|
||||||
|
<A, B, E2, R2>(f: (a: A) => Effect.Effect<B, E2, R2>): <E, R>(self: View<Option.Option<A>, E, R>) => View<Option.Option<B>, E | E2, R | R2>
|
||||||
|
<A, B, E, R, E2, R2>(self: View<Option.Option<A>, E, R>, f: (a: A) => Effect.Effect<B, E2, R2>): View<Option.Option<B>, E | E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, B, E, R, E2, R2>(
|
||||||
|
self: View<Option.Option<A>, E, R>,
|
||||||
|
f: (a: A) => Effect.Effect<B, E2, R2>,
|
||||||
|
) => mapEffect(self, Option.match({
|
||||||
|
onSome: a => Effect.map(f(a), Option.some),
|
||||||
|
onNone: () => Effect.succeed(Option.none()),
|
||||||
|
})))
|
||||||
|
|
||||||
|
|
||||||
|
/** Maps errors from both the current value and the stream of changes. */
|
||||||
|
export const mapError: {
|
||||||
|
<E, E2>(f: (error: NoInfer<E>) => E2): <A, R>(self: View<A, E, R>) => View<A, E2, R>
|
||||||
|
<A, E, R, E2>(self: View<A, E, R>, f: (error: NoInfer<E>) => E2): View<A, E2, R>
|
||||||
|
} = Function.dual(2, <A, E, R, E2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
f: (error: NoInfer<E>) => E2,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.mapError(self.get, f) },
|
||||||
|
get changes() { return Stream.mapError(self.changes, f) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Runs an effect when either the current value or the stream of changes fails. */
|
||||||
|
export const tapError: {
|
||||||
|
<E, B, E2, R2>(f: (error: NoInfer<E>) => Effect.Effect<B, E2, R2>): <A, R>(self: View<A, E, R>) => View<A, E | E2, R | R2>
|
||||||
|
<A, E, R, B, E2, R2>(self: View<A, E, R>, f: (error: NoInfer<E>) => Effect.Effect<B, E2, R2>): View<A, E | E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
f: (error: NoInfer<E>) => Effect.Effect<B, E2, R2>,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.tapError(self.get, f) },
|
||||||
|
get changes() { return Stream.tapError(self.changes, f) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
const catch_: {
|
||||||
|
<E, B, E2, R2>(f: (error: NoInfer<E>) => View<B, E2, R2>): <A, R>(self: View<A, E, R>) => View<A | B, E2, R | R2>
|
||||||
|
<A, E, R, B, E2, R2>(self: View<A, E, R>, f: (error: NoInfer<E>) => View<B, E2, R2>): View<A | B, E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
f: (error: NoInfer<E>) => View<B, E2, R2>,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.catch(self.get, error => f(error).get) },
|
||||||
|
get changes() { return Stream.catch(self.changes, error => f(error).changes) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Recovers from typed errors with another `View`. */
|
||||||
|
export { catch_ as catch }
|
||||||
|
|
||||||
|
/** Recovers from all failure causes with another `View`. */
|
||||||
|
export const catchCause: {
|
||||||
|
<E, B, E2, R2>(f: (cause: Cause.Cause<NoInfer<E>>) => View<B, E2, R2>): <A, R>(self: View<A, E, R>) => View<A | B, E2, R | R2>
|
||||||
|
<A, E, R, B, E2, R2>(self: View<A, E, R>, f: (cause: Cause.Cause<NoInfer<E>>) => View<B, E2, R2>): View<A | B, E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
f: (cause: Cause.Cause<NoInfer<E>>) => View<B, E2, R2>,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.catchCause(self.get, cause => f(cause).get) },
|
||||||
|
get changes() { return Stream.catchCause(self.changes, cause => f(cause).changes) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Runs an effect when either channel fails, exposing the complete failure cause. */
|
||||||
|
export const tapCause: {
|
||||||
|
<E, B, E2, R2>(f: (cause: Cause.Cause<NoInfer<E>>) => Effect.Effect<B, E2, R2>): <A, R>(self: View<A, E, R>) => View<A, E | E2, R | R2>
|
||||||
|
<A, E, R, B, E2, R2>(self: View<A, E, R>, f: (cause: Cause.Cause<NoInfer<E>>) => Effect.Effect<B, E2, R2>): View<A, E | E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
f: (cause: Cause.Cause<NoInfer<E>>) => Effect.Effect<B, E2, R2>,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.tapCause(self.get, f) },
|
||||||
|
get changes() { return Stream.tapCause(self.changes, f) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Falls back to another `View` when either channel fails. */
|
||||||
|
export const orElse: {
|
||||||
|
<B, E2, R2>(that: () => View<B, E2, R2>): <A, E, R>(self: View<A, E, R>) => View<A | B, E2, R | R2>
|
||||||
|
<A, E, R, B, E2, R2>(self: View<A, E, R>, that: () => View<B, E2, R2>): View<A | B, E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, B, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
that: () => View<B, E2, R2>,
|
||||||
|
) => catch_(self, that))
|
||||||
|
|
||||||
|
/** Replaces typed errors from either channel with a lazily evaluated value. */
|
||||||
|
export const orElseSucceed: {
|
||||||
|
<B>(value: () => B): <A, E, R>(self: View<A, E, R>) => View<A | B, never, R>
|
||||||
|
<A, E, R, B>(self: View<A, E, R>, value: () => B): View<A | B, never, R>
|
||||||
|
} = Function.dual(2, <A, E, R, B>(self: View<A, E, R>, value: () => B) => make({
|
||||||
|
get get() { return Effect.orElseSucceed(self.get, value) },
|
||||||
|
get changes() { return Stream.orElseSucceed(self.changes, value) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Retries failures from both channels according to the supplied schedule. */
|
||||||
|
export const retry: {
|
||||||
|
<E, X, E2, R2>(policy: Schedule.Schedule<X, NoInfer<E>, E2, R2>): <A, R>(self: View<A, E, R>) => View<A, E | E2, R | R2>
|
||||||
|
<A, E, R, X, E2, R2>(self: View<A, E, R>, policy: Schedule.Schedule<X, NoInfer<E>, E2, R2>): View<A, E | E2, R | R2>
|
||||||
|
} = Function.dual(2, <A, E, R, X, E2, R2>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
policy: Schedule.Schedule<X, NoInfer<E>, E2, R2>,
|
||||||
|
) => make({
|
||||||
|
get get() { return Effect.retry(self.get, policy) },
|
||||||
|
get changes() { return Stream.retry(self.changes, policy) },
|
||||||
|
}))
|
||||||
|
|
||||||
|
/** Converts typed failures from both channels into `Result` values. */
|
||||||
|
export const result = <A, E, R>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
): View<Result.Result<A, E>, never, R> => make({
|
||||||
|
get get() { return Effect.result(self.get) },
|
||||||
|
get changes() { return Stream.result(self.changes) },
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/** Narrows the focus to a field of an object. */
|
||||||
|
export const focusObjectOn: {
|
||||||
|
<A extends object, K extends keyof A>(key: K): <E, R>(self: View<A, E, R>) => View<A[K], E, R>
|
||||||
|
<A extends object, K extends keyof A, E, R>(self: View<A, E, R>, key: K): View<A[K], E, R>
|
||||||
|
} = Function.dual(2, <A extends object, K extends keyof A, E, R>(self: View<A, E, R>, key: K) =>
|
||||||
|
map(self, a => a[key]),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Narrows the focus to an indexed element of an array. */
|
||||||
|
export const focusArrayAt: {
|
||||||
|
<A extends readonly any[]>(index: number): <E, R>(self: View<A, E, R>) => View<A[number], E | Cause.NoSuchElementError, R>
|
||||||
|
<A extends readonly any[], E, R>(self: View<A, E, R>, index: number): View<A[number], E | Cause.NoSuchElementError, R>
|
||||||
|
} = Function.dual(2, <A extends readonly any[], E, R>(self: View<A, E, R>, index: number) =>
|
||||||
|
mapEffect(self, a => Effect.fromOption(Array.get(a, index))),
|
||||||
|
)
|
||||||
|
|
||||||
|
export const focusArrayLength = <A extends readonly any[], E, R>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
): View<number, E, R> => map(self, Array.length)
|
||||||
|
|
||||||
|
/** Narrows the focus to an indexed element of a readonly tuple. */
|
||||||
|
export const focusTupleAt: {
|
||||||
|
<T extends readonly [any, ...any[]], I extends number>(index: I): <E, R>(self: View<T, E, R>) => View<T[I], E, R>
|
||||||
|
<T extends readonly [any, ...any[]], I extends number, E, R>(self: View<T, E, R>, index: I): View<T[I], E, R>
|
||||||
|
} = Function.dual(2, <T extends readonly [any, ...any[]], I extends number, E, R>(self: View<T, E, R>, index: I) =>
|
||||||
|
map(self, Array.getUnsafe(index)),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Narrows the focus to an indexed element of `Chunk`. */
|
||||||
|
export const focusChunkAt: {
|
||||||
|
<A>(index: number): <E, R>(self: View<Chunk.Chunk<A>, E, R>) => View<A, E | Cause.NoSuchElementError, R>
|
||||||
|
<A, E, R>(self: View<Chunk.Chunk<A>, E, R>, index: number): View<A, E | Cause.NoSuchElementError, R>
|
||||||
|
} = Function.dual(2, <A, E, R>(self: View<Chunk.Chunk<A>, E, R>, index: number) =>
|
||||||
|
mapEffect(self, chunk => Effect.fromOption(Chunk.get(chunk, index))),
|
||||||
|
)
|
||||||
|
|
||||||
|
export const focusChunkSize = <A, E, R>(
|
||||||
|
self: View<Chunk.Chunk<A>, E, R>,
|
||||||
|
): View<number, E, R> => map(self, Chunk.size)
|
||||||
|
|
||||||
|
export const focusIterableSize = <A extends Iterable<any>, E, R>(
|
||||||
|
self: View<A, E, R>,
|
||||||
|
): View<number, E, R> => map(self, Iterable.size)
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * as Lens from "./Lens.js"
|
export * as Lens from "./Lens.js"
|
||||||
export * as Subscribable from "./Subscribable.js"
|
export * as View from "./View.js"
|
||||||
|
|||||||
Reference in New Issue
Block a user