Refactor Result

This commit is contained in:
Julien Valverdé
2026-01-14 10:29:52 +01:00
parent 7705880afe
commit 931511b890

View File

@@ -1,4 +1,4 @@
import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Option, Pipeable, Predicate, PubSub, pipe, Ref, type Scope, Stream, Subscribable } from "effect" import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, PubSub, pipe, Ref, type Scope, Stream, Subscribable } from "effect"
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result") export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
@@ -12,7 +12,7 @@ export type Result<A, E = never, P = never> = (
) )
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here // biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
export type Final<A, E = never, P = never> = (Success<A> | Failure<A, E>) & ({} | Flags<P>) export type Final<A, E = never, P = never> = (Success<A> | Failure<E>) & ({} | Flags<P>)
export type Flags<P = never> = WillFetch | WillRefresh | Refreshing<P> export type Flags<P = never> = WillFetch | WillRefresh | Refreshing<P>
export declare namespace Result { export declare namespace Result {
@@ -43,10 +43,9 @@ export interface Success<A> extends Result.Prototype {
readonly value: A readonly value: A
} }
export interface Failure<A, E = never> extends Result.Prototype { export interface Failure<E = never> extends Result.Prototype {
readonly _tag: "Failure" readonly _tag: "Failure"
readonly cause: Cause.Cause<E> readonly cause: Cause.Cause<E>
readonly previousSuccess: Option.Option<Success<A>>
} }
export interface WillFetch { export interface WillFetch {
@@ -76,7 +75,7 @@ const ResultPrototype = Object.freeze({
Match.tag("Initial", () => true), Match.tag("Initial", () => true),
Match.tag("Running", self => Equal.equals(self.progress, (that as Running<any>).progress)), Match.tag("Running", self => Equal.equals(self.progress, (that as Running<any>).progress)),
Match.tag("Success", self => Equal.equals(self.value, (that as Success<any>).value)), Match.tag("Success", self => Equal.equals(self.value, (that as Success<any>).value)),
Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure<any, any>).cause)), Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure<any>).cause)),
Match.exhaustive, Match.exhaustive,
) )
}, },
@@ -105,7 +104,7 @@ export const isFinal = (u: unknown): u is Final<unknown, unknown, unknown> => is
export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial" export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial"
export const isRunning = (u: unknown): u is Running<unknown> => isResult(u) && u._tag === "Running" export const isRunning = (u: unknown): u is Running<unknown> => isResult(u) && u._tag === "Running"
export const isSuccess = (u: unknown): u is Success<unknown> => isResult(u) && u._tag === "Success" export const isSuccess = (u: unknown): u is Success<unknown> => isResult(u) && u._tag === "Success"
export const isFailure = (u: unknown): u is Failure<unknown, unknown> => isResult(u) && u._tag === "Failure" export const isFailure = (u: unknown): u is Failure<unknown> => isResult(u) && u._tag === "Failure"
export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag") export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag")
export const hasWillFetchFlag = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillFetch" export const hasWillFetchFlag = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillFetch"
export const hasWillRefreshFlag = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillRefresh" export const hasWillRefreshFlag = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillRefresh"
@@ -117,15 +116,7 @@ export const initial: {
} = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype) } = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype)
export const running = <P = never>(progress?: P): Running<P> => Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype) export const running = <P = never>(progress?: P): Running<P> => Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype)
export const succeed = <A>(value: A): Success<A> => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype) export const succeed = <A>(value: A): Success<A> => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype)
export const fail = <E>(cause: Cause.Cause<E> ): Failure<E> => Object.setPrototypeOf({ _tag: "Failure", cause }, ResultPrototype)
export const fail = <E, A = never>(
cause: Cause.Cause<E>,
previousSuccess?: Success<NoInfer<A>>,
): Failure<A, E> => Object.setPrototypeOf({
_tag: "Failure",
cause,
previousSuccess: Option.fromNullable(previousSuccess),
}, ResultPrototype)
export const willFetch = <R extends Initial | Final<any, any, any>>( export const willFetch = <R extends Initial | Final<any, any, any>>(
result: R result: R
@@ -150,11 +141,8 @@ export const refreshing = <R extends Final<any, any, any>, P = never>(
) )
export const fromExit = <A, E>( export const fromExit = <A, E>(
exit: Exit.Exit<A, E>, exit: Exit.Exit<A, E>
previousSuccess?: Success<NoInfer<A>>, ): Success<A> | Failure<E> => exit._tag === "Success" ? succeed(exit.value) : fail(exit.cause)
): Success<A> | Failure<A, E> => exit._tag === "Success"
? succeed(exit.value)
: fail(exit.cause, previousSuccess)
export const toExit = <A, E, P>( export const toExit = <A, E, P>(
self: Result<A, E, P> self: Result<A, E, P>
@@ -219,7 +207,7 @@ export namespace unsafeForkEffect {
export interface Options<A, E, P> { export interface Options<A, E, P> {
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here // biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
readonly initial?: (Initial | Success<A> | Failure<A, E>) & ({} | WillFetch | WillRefresh) readonly initial?: (Initial | Success<A> | Failure<E>) & ({} | WillFetch | WillRefresh)
readonly initialProgress?: P readonly initialProgress?: P
} }
} }
@@ -242,10 +230,7 @@ export const unsafeForkEffect = <A, E, R, P = never>(
).pipe( ).pipe(
Effect.andThen(effect), Effect.andThen(effect),
Effect.onExit(exit => Effect.andThen( Effect.onExit(exit => Effect.andThen(
state.set(fromExit( state.set(fromExit(exit)),
exit,
isSuccess(options?.initial) ? options.initial : undefined),
),
Effect.forkScoped(PubSub.shutdown(pubsub)), Effect.forkScoped(PubSub.shutdown(pubsub)),
)), )),
)), )),