Effect CommunityEC
Effect Community3y ago
88 replies
WIP

Exploring the Concept of a `TaggedError` for Library APIs

Would something like a TaggedError make any sense?

The idea is that in the context of a library you may have building blocks which are also part of the public API and you may want to give users a choice to use Effect, which means that if they choose not to, you need to unwrap the Effect and throw.

So instead of creating a tagged object and then creating the error, you could create a tagged error from the start.
export const TaggedError = <Key extends string>(tag: Key) => 
    class CustomError<Meta extends object = never> extends Error {
        readonly _tag = tag
        readonly metadata: Meta
        constructor(...[message, meta]: ErrorArgs<Meta>) {
            const details = !meta ? '' : '\n' + JSON.stringify(meta, null, 2)
            super(message + details);
            this.name = this._tag;
            this.metadata = meta || {} as any
        }
    }

type ErrorArgs<T> =
    [T] extends [never]
    ? [message: string]
    : [message: string, metadata: T];


and have an unwrapEffect function which creates a variant of a building block that throws your tagged error unconditionally:
export const unwrapEffect = <Args extends any[], R>(f: EffectfulFn<Args, R>) =>
    (...args: Args): R => pipe(
        f(...args),
        Effect.either,
        Effect.runSync,
        E.match({
            onLeft: error => { throw error; },
            onRight: identity
        })
    );

type EffectfulFn<Args extends any[], R> =
    (...args: Args) => Effect.Effect<never, unknown, R>


Caveat is that you need to think of the error message where the failure happens, so you're not benefiting from decoupling the error management from the flow of the program so much, but since it's not a question of recovering from an error but only documenting it, maybe it's not that of a problem.

Any thougths?
Was this page helpful?