Skip to content

Instantly share code, notes, and snippets.

@Willmo36
Created May 10, 2021 16:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Willmo36/4f34a9095b7a81d54b2cd8bc2323c4fd to your computer and use it in GitHub Desktop.
Save Willmo36/4f34a9095b7a81d54b2cd8bc2323c4fd to your computer and use it in GitHub Desktop.
TypeScript GADT practice - Redux-saga like
import { hole, identity, pipe } from "@effect-ts/core/Function";
/** Redux Types */
/** Redux Types */
/** Redux Types */
type Reducer<S, A> = (s: S, a: A) => S;
type Store<S, A> = {
get: () => S;
dispatch: (a: A) => void;
};
/** DSL types */
/** DSL types */
/** DSL types */
type Effect<S, A, R> =
| Get<S, A, R>
| Push<S, A, R>
| Call<S, A, R>
| Delay<S, A, R>
| Chain<S, A, R>;
class Get<S, A, R> {
readonly _tag = "Get";
constructor(
readonly use: <X>(
f: (_: {
readonly _S: (_: S) => S;
readonly _A: (_: A) => A;
readonly _R: (_: S) => R;
}) => X
) => X
) {}
}
class Push<S, A, R> {
readonly _tag = "Push";
constructor(
readonly use: <X>(
f: <T>(_: {
readonly _S: (_: S) => S;
readonly _R: (_: unknown) => R;
readonly action: A;
}) => X
) => X
) {}
}
class Call<S, A, R> {
readonly _tag = "Call";
constructor(
readonly promiseFn: () => Promise<R>,
readonly _S: (_: S) => S,
readonly _A: (_: A) => A
) {}
}
class Delay<S,A,R> {
readonly _tag = "Delay";
constructor(
readonly ms: number,
readonly _R: (_: void) => R,
){}
}
class Chain<S, A, R> {
readonly _tag = "Chain";
constructor(
readonly use: <X>(
go: <R2>(_: {
readonly inputEffect: Effect<S, A, R2>;
readonly afb: (r2: R2) => Effect<S, A, R>;
}) => X
) => X
) // readonly _S: (_: S) => S
{}
}
/** DSL consturctors */
/** DSL consturctors */
/** DSL consturctors */
function makeConstructors<S, A>() {
function get(): Effect<S, A, S> {
return new Get<S, A, S>((go) =>
go({
_S: identity,
_A: identity,
_R: identity,
})
);
}
function push(action: A): Effect<S, A, void> {
return new Push((go) =>
go({
_R: identity,
_S: identity,
action,
})
);
}
function call<R>(promiseFn: () => Promise<R>): Effect<S, A, R> {
return new Call(promiseFn, identity, identity);
}
function delay(ms: number):Effect<S,A,void> {
return new Delay(ms, identity);
}
function chain<R1_, R2_>(
afb: (r1: R1_) => Effect<S, A, R2_>
): (effect: Effect<S, A, R1_>) => Effect<S, A, R2_> {
return (effect) => {
return new Chain<S, A, R2_>((g) => {
return g({
inputEffect: effect,
afb,
});
});
};
}
return { get, push, call, delay, chain };
}
/** DSL interpreter */
/** DSL interpreter */
/** DSL interpreter */
function run<S, A, R>(store: Store<S, A>, effect: Effect<S, A, R>): Promise<R> {
switch (effect._tag) {
case "Get":
return Promise.resolve(effect.use((f) => f._R(store.get())));
case "Push":
return Promise.resolve(
effect.use((f) => {
store.dispatch(f.action);
return f._R({});
})
);
case "Call":
return effect.promiseFn();
case "Delay":
return makeDelay(effect.ms).then(n => effect._R())
case "Chain":
return effect.use(async (ops) => {
const r = await run(store, ops.inputEffect);
return run(store, ops.afb(r));
});
}
}
/** DSL testing */
/** DSL testing */
/** DSL testing */
type S = { name: string };
type Ac = { _tag: "capitalize" } | { _tag: "set_name"; payload: string };
const capitalize = (): Ac => ({ _tag: "capitalize" });
const setName = (name: string): Ac => ({ _tag: "set_name", payload: name });
const testReducer: Reducer<S, Ac> = (state, action) => {
switch (action._tag) {
case "capitalize":
return { ...state, name: state.name.toUpperCase() };
case "set_name":
return { ...state, name: action.payload };
}
return state;
};
let state: S = { name: "max" };
const store: Store<S, Ac> = {
get: () => state,
dispatch: (action) => {
state = testReducer(state, action);
},
};
const makeDelay = (ms: number) => new Promise(res => {
setTimeout(() => {
res(void 0);
}, ms);
});
const delayLazyPromise = <A>(fn: () => Promise<A>): () => Promise<A> => {
return () => makeDelay(3000).then(fn);
}
const { get, push, call, chain, delay } = makeConstructors<S, Ac>();
const testProgram = pipe(
push(setName("Elmo")),
chain(() => push(capitalize())),
chain(() => get()),
chain(() => call(() => Promise.resolve(1232))),
chain(() => delay(1000))
);
run(store, testProgram).then((result) => {
result;
const stateNow = store.get();
stateNow;
console.log({stateNow, result})
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment