@matechs/effect
Advanced tools
Comparing version 0.0.15 to 0.0.16
@@ -1,6 +0,6 @@ | ||
import * as F from "fluture"; | ||
import * as Ei from "fp-ts/lib/Either"; | ||
import * as Op from "fp-ts/lib/Option"; | ||
import * as W from "waveguide/lib/wave"; | ||
import * as EX from "waveguide/lib/exit"; | ||
import { Monad3E } from "./overload"; | ||
import { Cancel } from "fluture"; | ||
import { MonadThrow3 } from "fp-ts/lib/MonadThrow"; | ||
@@ -10,2 +10,3 @@ import { Bifunctor3 } from "fp-ts/lib/Bifunctor"; | ||
import { Option } from "fp-ts/lib/Option"; | ||
export { done, abort, raise } from "waveguide/lib/exit"; | ||
export declare const URI = "matechs/Effect"; | ||
@@ -15,3 +16,3 @@ export declare type URI = typeof URI; | ||
export declare type NoErr = never; | ||
export declare type Effect<R, E, A> = (r: R) => F.FutureInstance<E, A>; | ||
export declare type Effect<R, E, A> = (r: R) => W.Wave<E, A>; | ||
declare module "fp-ts/lib/HKT" { | ||
@@ -34,9 +35,8 @@ interface URItoKind3<R, E, A> { | ||
export declare function error(message: string): Error; | ||
export declare function fromFuture<E, A>(f: F.FutureInstance<E, A>): Effect<NoEnv, E, A>; | ||
export declare function fromFuture<E, A>(f: W.Wave<E, A>): Effect<NoEnv, E, A>; | ||
export declare function right<A>(a: A): Effect<NoEnv, NoErr, A>; | ||
export declare function left<E>(e: E): Effect<NoEnv, E, never>; | ||
export declare function liftPromise<A, E>(f: () => Promise<A>): Effect<NoEnv, never, A>; | ||
export declare function liftIO<A>(f: () => A): Effect<NoEnv, never, A>; | ||
export declare function tryCatch<A, E>(f: () => Promise<A>, onLeft: (e: any) => E): Effect<NoEnv, E, A>; | ||
export declare function tryCatchIO<A, E>(f: () => A, onLeft: (e: any) => E): Effect<NoEnv, E, A>; | ||
export declare function syncTotal<A>(f: () => A): Effect<NoEnv, never, A>; | ||
export declare function tryCatchIO<E, A>(f: () => A, onLeft: (e: any) => E): Effect<NoEnv, E, A>; | ||
export declare function tryCatch<E, A>(f: () => Promise<A>, onLeft: (e: any) => E): Effect<NoEnv, E, A>; | ||
export declare function chainLeft<R, E, E2, A, R2>(ma: Effect<R, E, A>, onLeft: (e: E) => Effect<R2, E2, A>): Effect<R & R2, E2, A>; | ||
@@ -52,8 +52,7 @@ export declare function when(predicate: boolean): <R, E, A>(ma: Effect<R, E, A>) => Effect<R, E, Op.Option<A>>; | ||
export declare function sequenceP<R, E, A>(n: number, ops: Array<Effect<R, E, A>>): Effect<R, E, Array<A>>; | ||
export declare function run<E, A>(ma: Effect<NoEnv, E, A>): () => Promise<Ei.Either<E, A>>; | ||
export declare function run<E, A>(ma: Effect<NoEnv, E, A>): () => Promise<EX.Exit<E, A>>; | ||
export declare function promise<A>(ma: Effect<NoEnv, any, A>): Promise<A>; | ||
export declare function bracket<R, E, A, B, E2>(acquire: Effect<R, E, A>, use: (a: A) => Effect<R, E2, B>, release: (a: A, e: Ei.Either<E | E2, B>) => Effect<R, E, void>): Effect<R, E | E2, B>; | ||
export declare function fork<E, A>(res: (a: A) => void, rej: (e: E) => void): (op: Effect<NoEnv, E, A>) => Cancel; | ||
export declare function bracket<R, E, A, B, E2>(acquire: Effect<R, E, A>, use: (a: A) => Effect<R, E2, B>, release: (a: A) => Effect<R, E, void>): Effect<R, E | E2, B>; | ||
export declare function toTaskLike<R, E, A>(ma: Effect<R, E, A>): Effect<R, NoErr, Ei.Either<E, A>>; | ||
export declare function fromTaskLike<R, E, A>(ma: Effect<R, never, Ei.Either<E, A>>): Effect<R, E, A>; | ||
export declare function fromNullableM<R, E, A>(ma: Effect<R, E, A>): Effect<R, E, Option<A>>; |
@@ -22,22 +22,29 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var F = __importStar(require("fluture")); | ||
var Ei = __importStar(require("fp-ts/lib/Either")); | ||
var Op = __importStar(require("fp-ts/lib/Option")); | ||
var M = __importStar(require("deepmerge")); | ||
var W = __importStar(require("waveguide/lib/wave")); | ||
var Ar = __importStar(require("fp-ts/lib/Array")); | ||
var S = __importStar(require("waveguide/lib/semaphore")); | ||
var pipeable_1 = require("fp-ts/lib/pipeable"); | ||
var Option_1 = require("fp-ts/lib/Option"); | ||
var Do_1 = require("fp-ts-contrib/lib/Do"); | ||
var exit_1 = require("waveguide/lib/exit"); | ||
exports.done = exit_1.done; | ||
exports.abort = exit_1.abort; | ||
exports.raise = exit_1.raise; | ||
exports.URI = "matechs/Effect"; | ||
exports.effectMonad = { | ||
URI: exports.URI, | ||
of: function (a) { return function (_) { return F.resolve(a); }; }, | ||
map: function (fa, f) { return function (r) { return F.map(f)(fa(r)); }; }, | ||
ap: function (fab, fa) { return function (r) { return F.ap(fa(r))(fab(r)); }; }, | ||
of: function (a) { return function (_) { return W.wave.of(a); }; }, | ||
map: function (fa, f) { return function (r) { return W.wave.map(fa(r), f); }; }, | ||
ap: function (fab, fa) { return function (r) { return W.wave.ap(fab(r), fa(r)); }; }, | ||
chain: function (fa, f) { return function (r) { | ||
return F.chain(function (x) { return f(x)(r); })(fa(r)); | ||
return W.wave.chain(fa(r), function (x) { return f(x)(r); }); | ||
}; }, | ||
throwError: function (e) { return function (_) { return F.reject(e); }; }, | ||
bimap: function (fea, f, g) { return function (r) { return F.bimap(f)(g)(fea(r)); }; }, | ||
mapLeft: function (fea, f) { return function (r) { return F.mapRej(f)(fea(r)); }; } | ||
throwError: function (e) { return function (_) { return W.raiseError(e); }; }, | ||
bimap: function (fea, f, g) { return function (r) { return W.bimap(fea(r), f, g); }; }, | ||
mapLeft: function (fea, f) { return function (r) { return W.mapError(fea(r), f); }; } | ||
}; | ||
exports.concurrentEffectMonad = __assign(__assign({}, exports.effectMonad), { ap: function (fab, fa) { return function (r) { return F.ap(fa(r))(fab(r)); }; } }); | ||
exports.concurrentEffectMonad = __assign(__assign({}, exports.effectMonad), { ap: function (fab, fa) { return function (r) { return W.parWave.ap(fab(r), fa(r)); }; } }); | ||
exports.ap = (_a = pipeable_1.pipeable(exports.effectMonad), _a.ap), exports.apFirst = _a.apFirst, exports.apSecond = _a.apSecond, exports.chain = _a.chain, exports.chainFirst = _a.chainFirst, exports.flatten = _a.flatten, exports.map = _a.map, exports.bimap = _a.bimap, exports.filterOrElse = _a.filterOrElse, exports.fromEither = _a.fromEither, exports.fromOption = _a.fromOption, exports.fromPredicate = _a.fromPredicate, exports.mapLeft = _a.mapLeft; | ||
@@ -60,25 +67,30 @@ exports.parAp = (_b = pipeable_1.pipeable(exports.concurrentEffectMonad), _b.ap), exports.parApFirst = _b.apFirst, exports.parApSecond = _b.apSecond; | ||
function left(e) { | ||
return function (_) { return F.reject(e); }; | ||
return function (_) { return W.raiseError(e); }; | ||
} | ||
exports.left = left; | ||
function liftPromise(f) { | ||
return function (_) { return F.attemptP(f); }; | ||
function syncTotal(f) { | ||
return function (_) { return W.sync(f); }; | ||
} | ||
exports.liftPromise = liftPromise; | ||
function liftIO(f) { | ||
return function (_) { return F.encase(f)(exports.noEnv); }; | ||
exports.syncTotal = syncTotal; | ||
function tryCatchIO(f, onLeft) { | ||
return function (_) { | ||
return W.async(function (op) { | ||
try { | ||
op(Ei.right(f())); | ||
} | ||
catch (e) { | ||
op(Ei.left(onLeft(e))); | ||
} | ||
/* istanbul ignore next */ | ||
return function () { }; | ||
}); | ||
}; | ||
} | ||
exports.liftIO = liftIO; | ||
exports.tryCatchIO = tryCatchIO; | ||
function tryCatch(f, onLeft) { | ||
return function (_) { return F.mapRej(onLeft)(F.attemptP(f)); }; | ||
return function (_) { return W.mapError(W.fromPromise(f), onLeft); }; | ||
} | ||
exports.tryCatch = tryCatch; | ||
function tryCatchIO(f, onLeft) { | ||
return function (_) { return F.mapRej(onLeft)(F.attempt(f)); }; | ||
} | ||
exports.tryCatchIO = tryCatchIO; | ||
function chainLeft(ma, onLeft) { | ||
return function (r) { | ||
return pipeable_1.pipe(ma(r), F.chainRej(function (e) { return onLeft(e)(r); })); | ||
}; | ||
return function (r) { return W.chainError(ma(r), function (e) { return onLeft(e)(r); }); }; | ||
} | ||
@@ -116,3 +128,3 @@ exports.chainLeft = chainLeft; | ||
function access(f) { | ||
return function (r) { return F.resolve(f(r)); }; | ||
return function (r) { return W.wave.of(f(r)); }; | ||
} | ||
@@ -122,3 +134,11 @@ exports.access = access; | ||
function sequenceP(n, ops) { | ||
return function (r) { return F.parallel(n)(ops.map(function (op) { return op(r); })); }; | ||
return function (r) { | ||
return Do_1.Do(W.wave) | ||
.bind("sem", S.makeSemaphore(n)) | ||
.bindL("r", function (_a) { | ||
var sem = _a.sem; | ||
return Ar.array.traverse(W.parWave)(ops, function (op) { return sem.withPermit(op(r)); }); | ||
}) | ||
.return(function (s) { return s.r; }); | ||
}; | ||
} | ||
@@ -128,7 +148,7 @@ exports.sequenceP = sequenceP; | ||
function run(ma) { | ||
return function () { return F.promise(toTaskLike(ma)(exports.noEnv)); }; | ||
return function () { return W.runToPromiseExit(ma(exports.noEnv)); }; | ||
} | ||
exports.run = run; | ||
function promise(ma) { | ||
return F.promise(ma(exports.noEnv)); | ||
return W.runToPromise(ma(exports.noEnv)); | ||
} | ||
@@ -140,3 +160,3 @@ exports.promise = promise; | ||
return exports.effectMonad.chain(toTaskLike(use(a)), function (e) { | ||
return exports.effectMonad.chain(release(a, e), function () { | ||
return exports.effectMonad.chain(release(a), function () { | ||
return Ei.isLeft(e) ? left(e.left) : right(e.right); | ||
@@ -148,6 +168,2 @@ }); | ||
exports.bracket = bracket; | ||
function fork(res, rej) { | ||
return function (op) { return F.fork(rej)(res)(op(exports.noEnv)); }; | ||
} | ||
exports.fork = fork; | ||
/* Task-like converters, convert operations that can fail into non failing and vice versa */ | ||
@@ -154,0 +170,0 @@ function toTaskLike(ma) { |
{ | ||
"name": "@matechs/effect", | ||
"version": "0.0.15", | ||
"version": "0.0.16", | ||
"license": "MIT", | ||
@@ -33,7 +33,7 @@ "private": false, | ||
"deepmerge": "^4.2.2", | ||
"fluture": "^12.0.1", | ||
"fp-ts": "^2.1.2", | ||
"fp-ts-contrib": "^0.1.6" | ||
"fp-ts-contrib": "^0.1.6", | ||
"waveguide": "^1.3.0" | ||
}, | ||
"gitHead": "d95301604f8cb35e21abcd1d1312666a614e3a37" | ||
"gitHead": "8112954e787f86ddff9c50932677ad66183ec7d4" | ||
} |
138
README.md
@@ -6,60 +6,12 @@ # Introduction | ||
It aims to provide a strong fundational block to build typescript code in a more testable and standardized way. | ||
It aims to provide a strong foundational block to build typescript code in a more testable and standardized way. | ||
This library is composed at its core by the `@matechs/effect` package that exposes 2 effects | ||
- `type Effect<R, E, A> = (r: R) => FutureInstance<E, A>` | ||
- `type Effect<R, E, A> = (r: R) => Wave<E, A>` | ||
The underlying `FutureInstance` is provided by `Fluture`. | ||
The underlying `Wave` is provided by `Waveguide`. | ||
You can think of this type as a computation that requires an environment `R` to run. | ||
An important point to note in the implementation is the `src/overload.ts` file where we extend and define the new abstract types required to specialize `fp-ts` behaviour to respect the variance of `R` & `E`. | ||
The key difference is expressed in: | ||
```ts | ||
export interface Chain3E<F extends URIS3> extends Apply3<F> { | ||
readonly chain: <R, E, A, R2, E2, B>( | ||
fa: Kind3<F, R, E, A>, | ||
f: (a: A) => Kind3<F, R2, E2, B> | ||
) => Kind3<F, R & R2, E | E2, B>; | ||
} | ||
export interface Monad3E<M extends URIS3> extends Applicative3<M>, Chain3E<M> {} | ||
export interface PipeableChain3E<F extends URIS3> extends PipeableApply3E<F> { | ||
readonly chain: <R, E, A, B>( | ||
f: (a: A) => Kind3<F, R, E, B> | ||
) => <R2, E2>(ma: Kind3<F, R2, E2, A>) => Kind3<F, R & R2, E | E2, B>; | ||
readonly chainFirst: <R, E, A, B>( | ||
f: (a: A) => Kind3<F, R, E, B> | ||
) => <R2, E2>(ma: Kind3<F, R2, E2, A>) => Kind3<F, R & R2, E | E2, A>; | ||
readonly flatten: <R, E, R2, E2, A>( | ||
mma: Kind3<F, R, E, Kind3<F, R2, E2, A>> | ||
) => Kind3<F, R & R2, E | E2, A>; | ||
} | ||
export interface PipeableApply3E<F extends URIS3> extends PipeableFunctor3<F> { | ||
readonly ap: <R, E, A, R2, E2>( | ||
fa: Kind3<F, R, E, A> | ||
) => <B>(fab: Kind3<F, R2, E2, (a: A) => B>) => Kind3<F, R & R2, E | E2, B>; | ||
readonly apFirst: <R, E, B>( | ||
fb: Kind3<F, R, E, B> | ||
) => <A, R2, E2>(fa: Kind3<F, R2, E2, A>) => Kind3<F, R & R2, E | E2, A>; | ||
readonly apSecond: <R, E, B>( | ||
fb: Kind3<F, R, E, B> | ||
) => <A, R2, E2>(fa: Kind3<F, R2, E2, A>) => Kind3<F, R & R2, E | E2, B>; | ||
} | ||
export interface Apply3E<F extends URIS3> extends Functor3<F> { | ||
readonly ap: <R, E, A, B, R2, E2>( | ||
fab: Kind3<F, R, E, (a: A) => B>, | ||
fa: Kind3<F, R2, E2, A> | ||
) => Kind3<F, R & R2, E | E2, B>; | ||
} | ||
``` | ||
In addition to that the `fp-ts-contrib/lib/Do` module has similar overloads. This workaround is necessary because typescript's lack of variance annotation on generics. | ||
The module exposes 2 instances of the typeclass `type EffectMonad<T extends URIS3> = Monad3E<T> & MonadThrow3<T> & Bifunctor3<T>`: | ||
@@ -72,81 +24,29 @@ | ||
In addition to default implementations additional exposed functions are: | ||
Interesting integrations and usage examples can be found in `packages/orm`, `packages/http`, `packages/rpc`, `packages/tracing` | ||
```ts | ||
/* utils */ | ||
export function error(message: string) { | ||
return new Error(message); | ||
} | ||
## Details | ||
/* lift functions */ | ||
For details about the additional types and overloads please refer to documentation in `packages/effect` | ||
export function fromFuture<E, A>(f: F.FutureInstance<E, A>): Effect<NoEnv, E, A> | ||
## Notes | ||
export function right<A>(a: A): Effect<NoEnv, NoErr, A> | ||
This package is a work in progress syntax and functions might change, feedback are welcome and contributions even more! | ||
export function left<E>(e: E): Effect<NoEnv, E, never> | ||
The primary difference with waveguide itself is in how we manage the `R` parameter and in the utilities that we provide around environment management. The focus on this library is making environmental effects easy while providing valuable integrations out of the box where `Waveguide` itself poses primary focus around the underlying `Wave<E, A>` | ||
export function liftPromise<A, E>(f: () => Promise<A>): Effect<NoEnv, never, A> | ||
## Ecosystem | ||
export function liftIO<A>(f: () => A): Effect<NoEnv, never, A> | ||
- `@matechs/tracing` : provides integration with opentracing-js | ||
- `@matechs/http` : provides integration with axios | ||
- `@matechs/orm` : provides integration with typeorm | ||
- `@matechs/rpc` : no boilerplate rpc for your effects | ||
- `@matechs/express` : provides integration with express | ||
- `@matechs/graceful` : utility to handle graceful exit | ||
export function tryCatch<A, E>(f: () => Promise<A>, onLeft: (e: any) => E) | ||
## Thanks | ||
export function tryCatchIO<A, E>(f: () => A, onLeft: (e: any) => E) | ||
This library would have not been feasibly possible without the strong foundations of [fp-ts](https://github.com/gcanti/fp-ts) and [Waveguide](https://github.com/rzeigler/waveguide) & huge thanks to the Authors. | ||
export function chainLeft<R, E, E2, A, R2>(ma: Effect<R, E, A>,onLeft: (e: E) => Effect<R2, E2, A>) | ||
/* conditionals */ | ||
// run only when predicate is true, return in Option | ||
export function when(predicate: boolean): <R, E, A>(ma: Effect<R, E, A>) => Effect<R, E, Op.Option<A>> | ||
// same as alt but types are different so we return in Either | ||
export function or(predicate: boolean): <R, E, A>(ma: Effect<R, E, A>) => <R2, E2, B>(mb: Effect<R2, E2, B>) => Effect<R & R2, E | E2, Ei.Either<A, B>> | ||
// decide what to run depending on a boolean, both side returns same type | ||
export function alt(predicate: boolean): <R, E, A>(ma: Effect<R, E, A>) => (mb: Effect<R, E, A>) => Effect<R, E, A> | ||
/* manipulate environment */ | ||
export const noEnv = {}; // unit | ||
export function mergeEnv<A, B>(a: A): (b: B) => A & B // merge 2 environments | ||
export const provide = <R>(r: R) => <R2, E, A>(ma: Effect<R2 & R, E, A>): Effect<R2, E, A> // provide environment to an effect | ||
/* use environment */ | ||
export function accessM<R, R2, E, A>(f: (r: R) => Effect<R2, E, A>): Effect<R & R2, E, A> | ||
export function access<R, A>(f: (r: R) => A): Effect<R, NoErr, A> | ||
/* parallel */ | ||
export function sequenceP<R, E, A>(n: number, ops: Array<Effect<R, E, A>>): Effect<R, E, Array<A>> | ||
/* execution */ | ||
/* run an effect that requires no environment, return TaskEither */ | ||
function run<E, A>(ma: Effect<NoEnv, E, A>): () => Promise<Ei.Either<E, A>> | ||
/* run an effect that requires no environment, return Promise(reject on error) */ | ||
promise<A>(ma: Effect<NoEnv, any, A>): Promise<A> | ||
/* run an effect that requires no environment, return underlying Fluture fork */ | ||
fork<A, E>(res: (a: A) => void, rej: (e: E) => void): (ma: Effect<NoEnv, E, A>) => Cancel | ||
``` | ||
Interesting integrations and usage examples can be found in `packages/orm`, `packages/http`, `packages/rpc`, `packages/tracing` | ||
## Notes | ||
This package is a work in progress syntax and functions might change, feedback are welcome and contributions even more! | ||
## Thanks | ||
This library would have not been feasibly possible without the strong fundations of [fp-ts](https://github.com/gcanti/fp-ts) and [Fluture](https://github.com/fluture-js/Fluture) huge thanks to the Authors. | ||
Another huge thanks goes to both the scala community (ZIO in specific) and the haskell community (RIO & Polysemy) from which inspiration is taken. | ||
All of the above projects are advised! | ||
All of the above projects are advised! |
Sorry, the diff of this file is not supported yet
311
28582
51
+ Addedwaveguide@^1.3.0
+ Addedwaveguide@1.4.0(transitive)
- Removedfluture@^12.0.1
- Removedfluture@12.3.1(transitive)
- Removedsanctuary-show@2.0.0(transitive)
- Removedsanctuary-type-identifiers@3.0.0(transitive)