Skip to content

Instantly share code, notes, and snippets.

@titouancreach
Created April 23, 2025 20:58
Show Gist options
  • Save titouancreach/c86269f85758da1d882cf9e4eac23a10 to your computer and use it in GitHub Desktop.
Save titouancreach/c86269f85758da1d882cf9e4eac23a10 to your computer and use it in GitHub Desktop.
import { Data, Effect, Pipeable } from "effect";
export const TypeId: unique symbol = Symbol.for("TaggedHandler");
export type TypeId = typeof TypeId;
export interface TaggedHandler<T extends { _tag: string }, A, E, R>
extends Pipeable.Pipeable {
tags: Array<T["_tag"]>;
run: (value: T) => Effect.Effect<A, E, R>;
readonly [TypeId]: TypeId;
}
export const make = <T extends { _tag: string }, A, E, R>(
tag: T["_tag"],
handler: (value: T) => Effect.Effect<A, E, R>,
): TaggedHandler<T, A, E, R> => {
return {
tags: [tag],
run: handler,
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return Pipeable.pipeArguments(this, arguments);
},
[TypeId]: TypeId,
};
};
export const orElse = <
T1 extends { _tag: string },
T2 extends { _tag: string },
A,
E,
R,
A2 = A,
E2 = E,
R2 = R,
>(
handler1: TaggedHandler<T1, A, E, R>,
handler2: TaggedHandler<T2, A2, E2, R2>,
): TaggedHandler<T1 | T2, A | A2, E | E2, R | R2> => {
return {
tags: [...handler1.tags, ...handler2.tags],
run: (value: T1 | T2) => {
if (handler1.tags.includes(value._tag)) {
return handler1.run(value as T1) as Effect.Effect<
A | A2,
E | E2,
R | R2
>;
}
return handler2.run(value as T2) as Effect.Effect<A | A2, E | E2, R | R2>;
},
[TypeId]: TypeId,
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return Pipeable.pipeArguments(this, arguments);
},
};
};
// Helper pour combiner plusieurs handlers
export const combine = <T extends { _tag: string }, A, E, R>(
...handlers: Array<TaggedHandler<T, A, E, R>>
): TaggedHandler<T, A, E, R> => {
return handlers.reduce((acc, handler) => orElse(acc, handler));
};
export const mapEffect = <T extends { _tag: string }, A, E, R, A2>(
handler: TaggedHandler<T, A, E, R>,
fn: (value: A) => Effect.Effect<A2, E, R>,
): TaggedHandler<T, A2, E, R> => {
return {
tags: handler.tags,
run: (value) => handler.run(value).pipe(Effect.flatMap(fn)),
[TypeId]: TypeId,
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return Pipeable.pipeArguments(this, arguments);
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment