Skip to content

Commit

Permalink
feat: update navigation to fx v3
Browse files Browse the repository at this point in the history
  • Loading branch information
TylorS committed Dec 23, 2023
1 parent 14f3864 commit 606f5ec
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 126 deletions.
25 changes: 15 additions & 10 deletions packages/fx/src/Fx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ import { Cause, Effect, Either, Layer, Option, Queue, Schedule } from "effect"
import type { DurationInput } from "effect/Duration"
import { dual, identity } from "effect/Function"
import type * as Types from "effect/Types"
import * as strategies from "./internal/strategies.js"
import { TypeId } from "./TypeId.js"
import * as Emitter from "./Emitter.js"
import * as core from "./internal/core.js"
import * as coreKeyed from "./internal/keyed.js"
import * as coreShare from "./internal/share.js"
import * as strategies from "./internal/strategies.js"
import * as coreWithKey from "./internal/withKey.js"
import { type RefSubject, transform } from "./RefSubject.js"
import * as Sink from "./Sink.js"
import type * as Subject from "./Subject.js"
import { TypeId } from "./TypeId.js"

export interface Fx<out R, out E, out A> extends Pipeable.Pipeable {
readonly [TypeId]: Fx.Variance<R, E, A>
Expand Down Expand Up @@ -596,13 +596,13 @@ export const continueWith: {
} = dual(2, core.continueWith)

export const orElseCause: {
<E, R2, E2, B>(f: (cause: Cause.Cause<E>) => Fx<R2, E2, B>): <R, A>(fx: Fx<R, E, A>) => Fx<R | R2, E2, A | B>
<R, E, A, R2, E2, B>(fx: Fx<R, E, A>, f: (cause: Cause.Cause<E>) => Fx<R2, E2, B>): Fx<R | R2, E2, A | B>
<E, R2, E2, B>(f: (cause: Cause.Cause<E>) => Fx<R2, E2, B>): <R, A>(fx: Fx<R, E, A>) => Fx<R | R2, E2, A | B>
<R, E, A, R2, E2, B>(fx: Fx<R, E, A>, f: (cause: Cause.Cause<E>) => Fx<R2, E2, B>): Fx<R | R2, E2, A | B>
} = dual(2, core.orElseCause)

export const orElse: {
<E, R2, E2, B>(f: (error: E) => Fx<R2, E2, B>): <R, A>(fx: Fx<R, E, A>) => Fx<R | R2, E2, A | B>
<R, E, A, R2, E2, B>(fx: Fx<R, E, A>, f: (error: E) => Fx<R2, E2, B>): Fx<R | R2, E2, A | B>
<E, R2, E2, B>(f: (error: E) => Fx<R2, E2, B>): <R, A>(fx: Fx<R, E, A>) => Fx<R | R2, E2, A | B>
<R, E, A, R2, E2, B>(fx: Fx<R, E, A>, f: (error: E) => Fx<R2, E2, B>): Fx<R | R2, E2, A | B>
} = dual(2, core.orElse)

export const suspend: <R, E, A>(f: () => Fx<R, E, A>) => Fx<R, E, A> = core.suspend
Expand Down Expand Up @@ -927,8 +927,8 @@ export const multicast: <R, E, A>(fx: Fx<R, E, A>) => Fx<Scope.Scope | R, E, A>
export const hold: <R, E, A>(fx: Fx<R, E, A>) => Fx<Scope.Scope | R, E, A> = coreShare.hold

export const replay: {
(capacity: number): <R, E, A>(fx: Fx<R, E, A>) => Fx<Scope.Scope |R, E, A>
<R, E, A>(fx: Fx<R, E, A>, capacity: number): Fx<Scope.Scope |R, E, A>
(capacity: number): <R, E, A>(fx: Fx<R, E, A>) => Fx<Scope.Scope | R, E, A>
<R, E, A>(fx: Fx<R, E, A>, capacity: number): Fx<Scope.Scope | R, E, A>
} = dual(2, coreShare.replay)

export const mapCause: {
Expand Down Expand Up @@ -1660,8 +1660,13 @@ export const fromAsyncIterable: <A>(iterable: AsyncIterable<A>) => Fx<never, nev
* @since 1.20.0
*/
export const partitionMap: {
<A, B, C>(f: (a: A) => Either.Either<B, C>): <R, E>(fx: Fx<R, E, A>) => readonly [Fx<Scope.Scope | R, E, B>, Fx<Scope.Scope | R, E, C>]
<R, E, A, B, C>(fx: Fx<R, E, A>, f: (a: A) => Either.Either<B, C>): readonly [Fx<Scope.Scope | R, E, B>, Fx<Scope.Scope | R, E, C>]
<A, B, C>(
f: (a: A) => Either.Either<B, C>
): <R, E>(fx: Fx<R, E, A>) => readonly [Fx<Scope.Scope | R, E, B>, Fx<Scope.Scope | R, E, C>]
<R, E, A, B, C>(
fx: Fx<R, E, A>,
f: (a: A) => Either.Either<B, C>
): readonly [Fx<Scope.Scope | R, E, B>, Fx<Scope.Scope | R, E, C>]
} = dual(2, function partitionMap<R, E, A, B, C>(
fx: Fx<R, E, A>,
f: (a: A) => Either.Either<B, C>
Expand Down
9 changes: 6 additions & 3 deletions packages/fx/src/RefSubject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
import * as Boolean from "effect/Boolean"
import { dual } from "effect/Function"
import { sum } from "effect/Number"
import { ComputedTypeId, FilteredTypeId, RefSubjectTypeId, TypeId } from "./TypeId.js"
import type { Fx } from "./Fx.js"
import * as core from "./internal/core.js"
import * as DeferredRef from "./internal/DeferredRef.js"
Expand All @@ -27,6 +26,7 @@ import * as share from "./internal/share.js"
import type { UnionToTuple } from "./internal/UnionToTuple.js"
import * as Sink from "./Sink.js"
import * as Subject from "./Subject.js"
import { ComputedTypeId, FilteredTypeId, RefSubjectTypeId, TypeId } from "./TypeId.js"
import * as Versioned from "./Versioned.js"

const UNBOUNDED = { concurrency: "unbounded" } as const
Expand Down Expand Up @@ -112,8 +112,11 @@ export function fromFx<R, E, A>(
Effect.tap(({ core, deferredRef }) =>
Effect.forkIn(
fx.run(Sink.make(
(cause) => Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.failCause(cause))), () => core.subject.onFailure(cause)),
(value) => Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.succeed(value))), () => setCore(core, value))
(cause) =>
Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.failCause(cause))), () =>
core.subject.onFailure(cause)),
(value) =>
Effect.flatMap(Effect.sync(() => deferredRef.done(Exit.succeed(value))), () => setCore(core, value))
)),
core.scope
)
Expand Down
4 changes: 2 additions & 2 deletions packages/fx/src/internal/effect-operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type EffectOperatorFusionMap = {
const EffectOperatorFusionMap: EffectOperatorFusionMap = {
MapEffect: {
MapEffect: (op1, op2) => MapEffect((a: any) => Effect.flatMap(op1.f(a), op2.f)),
TapEffect: (op1, op2) => TapEffect((a: any) => Effect.tap(op1.f(a), op2.f)),
TapEffect: (op1, op2) => MapEffect((a: any) => Effect.tap(op1.f(a), op2.f)),
FilterEffect: (op1, op2) =>
FilterMapEffect((a: any) =>
Effect.flatMap(
Expand Down Expand Up @@ -123,7 +123,7 @@ const EffectOperatorFusionMap: EffectOperatorFusionMap = {
op1.f(a),
Option.match({
onNone: () => Effect.succeedNone,
onSome: (b) => op2.f(a).pipe(Effect.map(() => b))
onSome: (b) => Effect.as(op2.f(b), Option.some(b))
})
)
),
Expand Down
18 changes: 18 additions & 0 deletions packages/fx/test/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1065,4 +1065,22 @@ describe.concurrent("Fx.partitionMap", () => {
expect(odds).toEqual([1, 3])
expect(evens).toEqual([2, 4])
})

describe("fusion", () => {
describe("filterMap + tapEffect", () => {
it("works as expected", async () => {
const values: Array<number> = []
const test = Fx.fromIterable([1, 2, 3]).pipe(
Fx.filterMap(Option.liftPredicate((x) => x % 2 === 0)),
Fx.tapEffect((x) => Effect.sync(() => values.push(x))),
Fx.toReadonlyArray
)

const array = await Effect.runPromise(test)

expect(array).toEqual([2])
expect(values).toEqual([2])
})
})
})
})
24 changes: 13 additions & 11 deletions packages/navigation/src/Blocking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
* @since 1.0.0
*/

import type * as Computed from "@typed/fx/Computed"
import * as RefSubject from "@typed/fx/RefSubject"
import type { Scope } from "effect"
import { Deferred, Effect, Option } from "effect"
import { Data, Deferred, Effect, Option } from "effect"
import type {
BeforeNavigationEvent,
CancelNavigation,
Expand All @@ -18,8 +17,8 @@ import { cancelNavigation, Navigation, redirectToPath } from "./Navigation.js"
/**
* @since 1.0.0
*/
export interface BlockNavigation extends Computed.Computed<never, never, Option.Option<Blocking>> {
readonly isBlocking: Computed.Computed<never, never, boolean>
export interface BlockNavigation extends RefSubject.Computed<never, never, Option.Option<Blocking>> {
readonly isBlocking: RefSubject.Computed<never, never, boolean>
}

/**
Expand All @@ -36,7 +35,7 @@ type InternalBlockState = Unblocked | Blocked
type Unblocked = {
readonly _tag: "Unblocked"
}
const Unblocked: Unblocked = { _tag: "Unblocked" }
const Unblocked: Unblocked = Data.struct({ _tag: "Unblocked" })

type Blocked = {
readonly _tag: "Blocked"
Expand All @@ -47,7 +46,7 @@ type Blocked = {
const Blocked = (event: BeforeNavigationEvent) =>
Effect.map(
Deferred.make<RedirectError | CancelNavigation, void>(),
(deferred): Blocked => ({ _tag: "Blocked", deferred, event })
(deferred): Blocked => Data.struct({ _tag: "Blocked", deferred, event })
)

/**
Expand All @@ -69,7 +68,7 @@ export const useBlockNavigation = <R = never>(

yield* _(
navigation.beforeNavigation<R, never>((event) =>
blockState.modifyEffect((state) =>
RefSubject.modifyEffect(blockState, (state) =>
Effect.gen(function*(_) {
// Can't block twice
if (state._tag === "Blocked") return [Option.none(), state] as const
Expand All @@ -84,14 +83,17 @@ export const useBlockNavigation = <R = never>(
Option.some(Deferred.await(updated.deferred)),
updated
] as const
})
)
}))
)
)

const blockNavigation: BlockNavigation = Object.assign(
blockState.map((s) => s._tag === "Blocked" ? Option.some(blockedToBlocking(navigation, s)) : Option.none()),
{ isBlocking: blockState.map((s) => s._tag === "Blocked") }
RefSubject.map(blockState, (s) => {
return s._tag === "Blocked" ? Option.some(blockedToBlocking(navigation, s)) : Option.none()
}),
{
isBlocking: RefSubject.map(blockState, (s) => s._tag === "Blocked")
}
)

return blockNavigation
Expand Down
32 changes: 17 additions & 15 deletions packages/navigation/src/Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { ParseResult } from "@effect/schema"
import * as Schema from "@effect/schema/Schema"
import { Tagged } from "@typed/context"
import * as Computed from "@typed/fx/Computed"
import * as RefSubject from "@typed/fx/RefSubject"
import type { Uuid } from "@typed/id"
import * as IdSchema from "@typed/id/Schema"
import type { Option, Scope } from "effect"
Expand All @@ -19,15 +19,15 @@ export interface Navigation {

readonly base: string

readonly currentEntry: Computed.Computed<never, never, Destination>
readonly currentEntry: RefSubject.Computed<never, never, Destination>

readonly entries: Computed.Computed<never, never, ReadonlyArray<Destination>>
readonly entries: RefSubject.Computed<never, never, ReadonlyArray<Destination>>

readonly transition: Computed.Computed<never, never, Option.Option<Transition>>
readonly transition: RefSubject.Computed<never, never, Option.Option<Transition>>

readonly canGoBack: Computed.Computed<never, never, boolean>
readonly canGoBack: RefSubject.Computed<never, never, boolean>

readonly canGoForward: Computed.Computed<never, never, boolean>
readonly canGoForward: RefSubject.Computed<never, never, boolean>

readonly navigate: (
url: string | URL,
Expand Down Expand Up @@ -324,7 +324,7 @@ export const reload: (
/**
* @since 1.0.0
*/
export const CurrentEntry: Computed.Computed<Navigation, never, Destination> = Computed.fromTag(
export const CurrentEntry: RefSubject.Computed<Navigation, never, Destination> = RefSubject.computedFromTag(
Navigation,
(nav) => nav.currentEntry
)
Expand All @@ -339,30 +339,32 @@ export function getCurrentPathFromUrl(location: Pick<URL, "pathname" | "search"
/**
* @since 1.0.0
*/
export const CurrentPath: Computed.Computed<Navigation, never, string> = CurrentEntry.map((d) =>
getCurrentPathFromUrl(d.url)
export const CurrentPath: RefSubject.Computed<Navigation, never, string> = RefSubject.map(
CurrentEntry,
(d) => getCurrentPathFromUrl(d.url)
)

/**
* @since 1.0.0
*/
export const CurrentEntries: Computed.Computed<Navigation, never, ReadonlyArray<Destination>> = Computed.fromTag(
Navigation,
(n) => n.entries
)
export const CurrentEntries: RefSubject.Computed<Navigation, never, ReadonlyArray<Destination>> = RefSubject
.computedFromTag(
Navigation,
(n) => n.entries
)

/**
* @since 1.0.0
*/
export const CanGoForward: Computed.Computed<Navigation, never, boolean> = Computed.fromTag(
export const CanGoForward: RefSubject.Computed<Navigation, never, boolean> = RefSubject.computedFromTag(
Navigation,
(n) => n.canGoForward
)

/**
* @since 1.0.0
*/
export const CanGoBack: Computed.Computed<Navigation, never, boolean> = Computed.fromTag(
export const CanGoBack: RefSubject.Computed<Navigation, never, boolean> = RefSubject.computedFromTag(
Navigation,
(n) => n.canGoBack
)
Expand Down
Loading

0 comments on commit 606f5ec

Please sign in to comment.