Last active
May 4, 2020 18:30
-
-
Save safareli/4e0f8fcc85065c270d7269828cc306f3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// prototype of making effect Readerish | |
// https://gist.github.com/safareli/7f60873324cce8880fb4cfa85defe4df | |
export interface EffectRaw<E,A> { (env: E): A } | |
interface EffectPURE<E, A> extends EffectRaw<E,A> { | |
tag: "PURE", | |
_0: A | |
} | |
interface EffectMAP<E, Z, A> extends EffectRaw<E, A>, EffectMAP_OP<Z, A> { | |
_1: Effect<E, Z> | |
} | |
interface EffectAPPLY<E, Z, A> extends EffectRaw<E, A>, EffectAPPLY_OP<E,Z> { | |
_1: Effect<E, ((val: Z) => A)> | |
} | |
interface EffectBIND<E, Z, A> extends EffectRaw<E, A>, EffectBIND_OP<E,Z, A> { | |
_1: Effect<E, Z> | |
} | |
interface EffectLOCAL<E, Z, A> extends EffectRaw<E, A>, EffectLOCAL_OP<E,Z> { | |
_1: Effect<Z, A> | |
} | |
export type Effect<E, A> = EffectRaw<E, A> | EffectTagged<E, A> | |
type EffectTagged<E, A> | |
= EffectPURE<E, A> | |
| EffectMAP<E, unknown, A> | |
| EffectLOCAL<E, unknown, A> | |
| EffectAPPLY<E, unknown, A> | |
| EffectBIND<E, unknown, A> | |
export const pure = function <E,A>(x: A): EffectPURE<E,A> { | |
const res: EffectPURE<E,A> = function $effect(env: E): A { return runEff($effect, env); }; | |
res.tag = "PURE"; | |
res._0 = x; | |
return res; | |
}; | |
export const map = function <E,Z, A>(f: ((val: Z) => A)) { | |
return function (effect: Effect<E,Z>): EffectMAP<E,Z, A> { | |
const res: EffectMAP<E,Z, A> = function $effect(env: E): A { return runEff($effect, env); }; | |
res.tag = "MAP"; | |
res._0 = f; | |
res._1 = effect; | |
return res; | |
}; | |
}; | |
export const apply = function <E,Z, A>(effF: Effect<E,((val: Z) => A)>) { | |
return function (effect: Effect<E,Z>): EffectAPPLY<E,Z, A> { | |
const res: EffectAPPLY<E,Z, A> = function $effect(env: E): A { return runEff($effect, env); }; | |
res.tag = "APPLY"; | |
res._0 = effect; | |
res._1 = effF; | |
return res; | |
}; | |
}; | |
export const bind = function <E,Z, A>(effect: Effect<E,Z>) { | |
return function (f: (val: Z) => Effect<E,A>): EffectBIND<E,Z, A> { | |
const res: EffectBIND<E,Z, A> = function $effect(env: E): A { return runEff($effect, env); }; | |
res.tag = "BIND"; | |
res._0 = f; | |
res._1 = effect; | |
return res; | |
}; | |
}; | |
export const ask: <E>() => Effect<E, E> = () => <E>(env: E): E => env; | |
export const local = function <E,Z, A>(f: ((env: E) => Z)) { | |
return function (effect: Effect<Z,A>): EffectLOCAL<E, Z, A> { | |
const res: EffectLOCAL<E, Z, A> = function $effect(env: E): A { return runEff($effect, env); }; | |
res.tag = "LOCAL"; | |
res._0 = f; | |
res._1 = effect; | |
return res; | |
}; | |
}; | |
interface EffectAPPLY_FUNC_OP<Z, A> { tag: "APPLY_FUNC", _0: (v: Z) => A } | |
interface EffectMAP_OP<Z, A> { tag: "MAP", _0: (v: Z) => A } | |
interface EffectAPPLY_OP<E, Z> { tag: "APPLY", _0: Effect<E, Z> } | |
interface EffectBIND_OP<E, Z, A> { tag: "BIND", _0: (v: Z) => Effect<E, A> } | |
interface EffectLOCAL_UNDO_OP<Z> { tag: "LOCAL_UNDO", _0: Z } | |
interface EffectLOCAL_OP<E,Z>{ tag: "LOCAL", _0: (env: E) => Z } | |
type Effect_OP<E, Z, A> | |
= EffectAPPLY_FUNC_OP<Z, A> | |
| EffectMAP_OP<Z, A> | |
| EffectLOCAL_OP<E, Z> | |
| EffectLOCAL_UNDO_OP<Z> | |
| EffectAPPLY_OP<E, Z> | |
| EffectBIND_OP<E, Z, A> | |
function runEff<E, A>(inputEff: Effect<E, A>, env: E): A { | |
const operations: Array<Effect_OP<E, unknown, unknown>> = []; | |
let effect: Effect<E,unknown> = inputEff; | |
let res: unknown | undefined; | |
let op: Effect_OP<E,unknown, unknown> | undefined; | |
effLoop: for (; ;) { | |
if ("tag" in effect) { | |
switch (effect.tag) { | |
case "MAP": | |
operations.push(effect); | |
effect = effect._1; | |
continue; | |
case "LOCAL": | |
operations.push(effect); | |
effect = effect._1; | |
break; | |
case "BIND": | |
operations.push(effect); | |
effect = effect._1; | |
continue; | |
case "APPLY": | |
operations.push(effect); | |
effect = effect._1; | |
continue; | |
case "PURE": | |
res = effect._0; | |
break; | |
} | |
} else { | |
res = effect(env); | |
} | |
while ((op = operations.pop())) { | |
switch (op.tag) { | |
case "MAP": | |
res = op._0(res); | |
break | |
case "LOCAL": | |
operations.push({ tag: "LOCAL_UNDO", _0: env}); | |
env = op._0(env) as any; | |
continue effLoop; | |
case "LOCAL_UNDO": | |
env = op._0 as any; | |
break | |
// or maybe: | |
// continue effLoop; | |
case "APPLY_FUNC": | |
res = op._0(res); | |
break | |
case "APPLY": | |
effect = op._0; | |
operations.push({ tag: "APPLY_FUNC", _0: res as (v: unknown) => unknown }); | |
continue effLoop; | |
case "BIND": | |
effect = op._0(res); | |
continue effLoop; | |
} | |
} | |
return res as A; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment