Play with Effect

Core functions available to create and transform effects

Module

// pure value
export function pure<A>(a: A): Effect<NoEnv, NoErr, A>

// create an errored effect
export function raised<E, A = never>(e: Cause<E>): Effect<NoEnv, E, A>

// raise an error (like "throw")
export function raiseError<E, A = never>(e: E): Effect<NoEnv, E, A>

// raise an abort reason (unknown error)
export function raiseAbort(u: unknown): Effect<NoEnv, NoErr, never>

// raise an interruption signal in the execution fiber
export const raiseInterrupt: Effect<NoEnv, NoErr, never>

// create a completed effect with the same exit as the one provided
export function completed<E, A>(exit: Exit<E, A>): Effect<NoEnv, E, A>

// suspend execution of the thunk (give space for interrupt)
export function suspended<R, E, A>(
  thunk: Lazy<Effect<R, E, A>>
): Effect<R, E, A>

// encase a sync computation
export function sync<E = NoErr, A = unknown>(
  thunk: Lazy<A>
): Effect<NoEnv, E, A>

// try a sync computation
export function trySync<E = unknown, A = unknown>(
  thunk: Lazy<A>
): Effect<NoEnv, E, A>

// try a sync computation mapping the catched exception
export function trySyncMap<E = unknown>(
  onError: (e: unknown) => E
): <A = unknown>(thunk: Lazy<A>) => Effect<NoEnv, E, A> 

// wrap an async callback into an effect, op return Lazy is triggered
// on cancel
// type AsyncContFn<E, A> = F.FunctionN<[Ei.Either<E, A>], void>;
// type AsyncCancelContFn = F.FunctionN<[(error?: Error) => void], void>;
// type AsyncFn<E, A> = F.FunctionN<[AsyncContFn<E, A>], AsyncCancelContFn>;
export function async<E, A>(op: AsyncFn<E, A>): Effect<NoEnv, E, A>

// wrap an async callback into an effect that cannot fail
export function asyncTotal<A>(
  op: FunctionN<[FunctionN<[A], void>], Lazy<void>>
): Effect<NoEnv, NoErr, A>

// denote a region (interruptible or not)
export function interruptibleRegion<R, E, A>(
  inner: Effect<R, E, A>,
  flag: boolean
): Effect<R, E, A>

// encase an either into an effect
export function encaseEither<E, A>(e: Either<E, A>): Effect<NoEnv, E, A>

// encase an option into an effect
export function encaseOption<E, A>(
  o: Option<A>,
  onError: Lazy<E>
): Effect<NoEnv, E, A>

// encase nullable pure value
export function fromNullableM<R, E, A>(
  ma: Effect<R, E, A>
): Effect<R, E, Option<A>>

// encase promise
export function fromPromise<A>(
  thunk: Lazy<Promise<A>>
): Effect<NoEnv, unknown, A>

// encase promise mapping error
export function fromPromiseMap<E>(
  onError: (e: unknown) => E
): <A>(thunk: Lazy<Promise<A>>) => Effect<NoEnv, E, A>

// fold an effect handling success and failure
export function foldExit<E1, RF, E2, A1, E3, A2, RS>(
  failure: FunctionN<[Cause<E1>], Effect<RF, E2, A2>>,
  success: FunctionN<[A1], Effect<RS, E3, A2>>
): <R>(io: Effect<R, E1, A1>) => Effect<RF & RS & R, E2 | E3, A2>

// list a function into an effect
export function lift<A, B>(
  f: FunctionN<[A], B>
): <R, E>(io: Effect<R, E, A>) => Effect<R, E, B>

// map to constant
export function as<R, E, A, B>(io: Effect<R, E, A>, b: B): Effect<R, E, B>

export function to<B>(b: B): <R, E, A>(io: Effect<R, E, A>) => Effect<R, E, B>

// chain without changing outout
export function chainTap<R, E, A>(
  bind: FunctionN<[A], Effect<R, E, unknown>>
): <R2, E2>(inner: Effect<R2, E2, A>) => Effect<R & R2, E | E2, A>

// as unit
export function asUnit<R, E, A>(io: Effect<R, E, A>): Effect<R, E, void>

// unit
export const unit: Effect<NoEnv, NoErr, void>

// handle raised error
export function chainError<R, E1, E2, A>(
  f: FunctionN<[E1], Effect<R, E2, A>>
): <R2>(rio: Effect<R2, E1, A>) => Effect<R & R2, E2, A>

// map raised error
export function mapError<E1, E2>(
  f: FunctionN<[E1], E2>
): <R, A>(io: Effect<R, E1, A>) => Effect<R, E2, A>

// zip two effects with a function
export function zipWith<R, E, A, R2, E2, B, C>(
  first: Effect<R, E, A>,
  second: Effect<R2, E2, B>,
  f: FunctionN<[A, B], C>
): Effect<R & R2, E | E2, C>

// zip with identity
export function zip<R, E, A, R2, E2, B>(
  first: Effect<R, E, A>,
  second: Effect<R2, E2, B>
): Effect<R & R2, E | E2, readonly [A, B]>

// zip with first
export function applyFirst<R, E, A, R2, E2, B>(
  first: Effect<R, E, A>,
  second: Effect<R2, E2, B>
): Effect<R & R2, E | E2, A>

// zip with second
export function applySecond<R, E, A, R2, E2, B>(
  first: Effect<R, E, A>,
  second: Effect<R2, E2, B>
): Effect<R & R2, E | E2, B>

// zip with second lazy
export function applySecondL<R, E, A, R2, E2, B>(
  first: Effect<R, E, A>,
  second: Lazy<Effect<R2, E2, B>>
): Effect<R & R2, E | E2, B>

// flipped apply
export function ap__<R, E, A, R2, E2, B>(
  ioa: Effect<R, E, A>,
  iof: Effect<R2, E2, FunctionN<[A], B>>
): Effect<R & R2, E | E2, B>

// parallel zip equivalents
export function parZipWith<R, R2, E, E2, A, B, C>(
  ioa: Effect<R, E, A>,
  iob: Effect<R2, E2, B>,
  f: FunctionN<[A, B], C>
): Effect<R & R2, E | E2, C>

export function parZip<R, R2, E, A, B>(
  ioa: Effect<R, E, A>,
  iob: Effect<R2, E, B>
): Effect<R & R2, E, readonly [A, B]>

export function parApplyFirst<R, R2, E, A, B>(
  ioa: Effect<R, E, A>,
  iob: Effect<R2, E, B>
): Effect<R & R2, E, A>

export function parApplySecond<R, R2, E, A, B>(
  ioa: Effect<R, E, A>,
  iob: Effect<R2, E, B>
): Effect<R & R2, E, B>

export function parAp<R, R2, E, A, B>(
  ioa: Effect<R, E, A>,
  iof: Effect<R2, E, FunctionN<[A], B>>
): Effect<R & R2, E, B>

export function parAp_<R, R2, E, E2, A, B>(
  iof: Effect<R, E, FunctionN<[A], B>>,
  ioa: Effect<R2, E2, A>
): Effect<R & R2, E | E2, B>

// flip A and E
export function flip<R, E, A>(io: Effect<R, E, A>): Effect<R, A, E>

// execute forever or until failure
export function forever<R, E, A>(io: Effect<R, E, A>): Effect<R, E, A>

// get the result of executing io
export function result<R, E, A>(
  io: Effect<R, E, A>
): Effect<R, NoErr, Exit<E, A>>

// make io interruptible
export function interruptible<R, E, A>(io: Effect<R, E, A>): Effect<R, E, A>

// make io non interruptible
export function uninterruptible<R, E, A>(io: Effect<R, E, A>): Effect<R, E, A>

// bracket with full exit control
export function bracketExit<R, E, A, B, R2, E2, R3, E3>(
  acquire: Effect<R, E, A>,
  release: FunctionN<[A, Exit<E | E3, B>], Effect<R2, E2, unknown>>,
  use: FunctionN<[A], Effect<R3, E3, B>>
): Effect<R & R2 & R3, E | E2 | E3, B>

// bracket not dealing with use result
export function bracket<R, E, A, R2, E2, R3, E3, B>(
  acquire: Effect<R, E, A>,
  release: FunctionN<[A], Effect<R2, E2, unknown>>,
  use: FunctionN<[A], Effect<R3, E3, B>>
): Effect<R & R2 & R3, E | E2 | E3, B>

// run finalizer on complete
export function onComplete<R, E, A, R2, E2>(
  ioa: Effect<R, E, A>,
  finalizer: Effect<R2, E2, unknown>
): Effect<R & R2, E | E2, A>
// run finalizer on interrupt
export function onInterrupted<R, E, A, R2, E2>(
  ioa: Effect<R, E, A>,
  finalizer: Effect<R2, E2, unknown>
): Effect<R & R2, E | E2, A>

// introduce a gap to allow other fibers to execute
export const shifted: Effect<NoEnv, NoErr, void>

// add shift before io
export function shiftBefore<E, A>(
  io: Effect<NoEnv, E, A>
): Effect<NoEnv, E, A>

// add shift after io
export function shiftAfter<E, A>(io: Effect<NoEnv, E, A>): Effect<NoEnv, E, A>

// shift and return control to js thread
export const shiftedAsync: Effect<NoEnv, NoErr, void>

export function shiftAsyncBefore<R, E, A>(
  io: Effect<R, E, A>
): Effect<R, E, A>

export function shiftAsyncAfter<R, E, A>(io: Effect<R, E, A>): Effect<R, E, A>

// never return
export const never: Effect<NoEnv, NoErr, never>

// add delay to effect
export function delay<R, E, A>(
  inner: Effect<R, E, A>,
  ms: number
): Effect<R, E, A>

export function liftDelay(
  ms: number
): <R, E, A>(io: Effect<R, E, A>) => Effect<R, E, A>

// fork into new fiber
export function fork<R, E, A>(
  io: Effect<R, E, A>,
  name?: string
): Effect<R, NoErr, Fiber<E, A>>

// race and fold
export function raceFold<R, R2, R3, R4, E1, E2, E3, A, B, C>(
  first: Effect<R, E1, A>,
  second: Effect<R2, E2, B>,
  onFirstWon: FunctionN<[Exit<E1, A>, Fiber<E2, B>], Effect<R3, E3, C>>,
  onSecondWon: FunctionN<[Exit<E2, B>, Fiber<E1, A>], Effect<R4, E3, C>>
): Effect<R & R2 & R3 & R4, E3, C>

// race with timeout
export function timeoutFold<R, E1, E2, A, B>(
  source: Effect<R, E1, A>,
  ms: number,
  onTimeout: FunctionN<[Fiber<E1, A>], Effect<NoEnv, E2, B>>,
  onCompleted: FunctionN<[Exit<E1, A>], Effect<NoEnv, E2, B>>
): Effect<R, E2, B>

// race and get first (that completes or error)
export function raceFirst<R, R2, E, A>(
  io1: Effect<R, E, A>,
  io2: Effect<R2, E, A>
): Effect<R & R2, E, A>

// race and get first success
export function race<R, R2, E, A>(
  io1: Effect<R, E, A>,
  io2: Effect<R2, E, A>
): Effect<R & R2, E, A>

// convert error to abort
export function orAbort<R, E, A>(io: Effect<R, E, A>): Effect<R, NoErr, A> 

// timeout returning option of result
export function timeoutOption<R, E, A>(
  source: Effect<R, E, A>,
  ms: number
): Effect<R, E, Option<A>>
// run effect calling callback on completion
export function run<E, A>(
  io: Effect<{}, E, A>,
  callback?: FunctionN<[Exit<E, A>], void>
): Lazy<void>

// run effect as failable promise
export function runToPromise<E, A>(io: Effect<{}, E, A>): Promise<A>

// run effect as non failable promise of exit result
export function runToPromiseExit<E, A>(
  io: Effect<{}, E, A>
): Promise<Exit<E, A>>

// alternatively run fy if fx fails
export function alt<R2, E2, A>(
  fy: () => Effect<R2, E2, A>
): <R, E>(fx: Effect<R, E, A>) => Effect<R & R2, E | E2, A>

// instances
export const effect: EffectMonad = {
  URI,
  map: map_,
  of: pure,
  ap: ap_,
  chain: chain_,
  bimap: bimap_,
  mapLeft: mapLeft_,
  mapError: mapLeft_,
  throwError: raiseError,
  chainError: chainError_,
  foldExit: foldExit_,
  chainTap: chainTap_,
  alt: alt_
};

export const parEffect: Monad3E<URI> & Bifunctor3<URI> & MonadThrow3E<URI> = {
  URI,
  map: map_,
  of: pure,
  ap: parAp_,
  chain: chain_,
  bimap: bimap_,
  mapLeft: mapLeft_,
  throwError: raiseError
};

// pipeable combinators
const {
  ap,
  apFirst,
  apSecond,
  bimap,
  chain,
  chainFirst,
  filterOrElse,
  flatten,
  fromEither,
  fromOption,
  fromPredicate,
  map,
  mapLeft
} = pipeable(effect);

// get a semigroup to combine results
export function getSemigroup<R, E, A>(
  s: Semigroup<A>
): Semigroup<Effect<R, E, A>>

// get a monoid for result
export function getMonoid<R, E, A>(m: Monoid<A>): Monoid<Effect<R, E, A>>

/* conditionals */
export function when(
  predicate: boolean
): <R, E, A>(ma: Effect<R, E, A>) => Effect<R, E, Op.Option<A>>

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>>

export function or<R, E, A>(
  ma: Effect<R, E, A>
): <R2, E2, B>(
  mb: Effect<R2, E2, B>
) => (predicate: boolean) => Effect<R & R2, E | E2, Ei.Either<A, B>>

export function or<R, E, A>(
  ma: Effect<R, E, A>
): <R2, E2, B>(
  mb: Effect<R2, E2, B>
) => (predicate: boolean) => Effect<R & R2, E | E2, Ei.Either<A, B>>

export function cond<R, E, A>(
  ma: Effect<R, E, A>
): <R2, E2, B>(
  mb: Effect<R2, E2, B>
) => (predicate: boolean) => Effect<R & R2, E | E2, A | B>

// parallel sequence
export function sequenceP(
  n: number
): <R, E, A>(ops: Array<Effect<R, E, A>>) => Effect<R, E, Array<A>>

// given semigroup for raised error get a semigroup of cause
export function getCauseSemigroup<E>(S: Semigroup<E>): Semigroup<Cause<E>>

// get validation monad given semigroup of error
export function getValidationM<E>(S: Semigroup<E>)

// get validation monad given semigroup of couse
export function getCauseValidationM<E>(
  S: Semigroup<Cause<E>>
): Monad3EC<URI, E> & MonadThrow3EC<URI, E> & Alt3EC<URI, E>

// wrap node standard (inspired by fp-ts taskify)
export function effectify<L, R>(
  f: (cb: (e: L | null | undefined, r?: R) => void) => void
): () => Effect<NoEnv, L, R>;
export function effectify<A, L, R>(
  f: (a: A, cb: (e: L | null | undefined, r?: R) => void) => void
): (a: A) => Effect<NoEnv, L, R>;
export function effectify<A, B, L, R>(
  f: (a: A, b: B, cb: (e: L | null | undefined, r?: R) => void) => void
): (a: A, b: B) => Effect<NoEnv, L, R>;
export function effectify<A, B, C, L, R>(
  f: (a: A, b: B, c: C, cb: (e: L | null | undefined, r?: R) => void) => void
): (a: A, b: B, c: C) => Effect<NoEnv, L, R>;
export function effectify<A, B, C, D, L, R>(
  f: (
    a: A,
    b: B,
    c: C,
    d: D,
    cb: (e: L | null | undefined, r?: R) => void
  ) => void
): (a: A, b: B, c: C, d: D) => Effect<NoEnv, L, R>;
export function effectify<A, B, C, D, E, L, R>(
  f: (
    a: A,
    b: B,
    c: C,
    d: D,
    e: E,
    cb: (e: L | null | undefined, r?: R) => void
  ) => void
): (a: A, b: B, c: C, d: D, e: E) => Effect<NoEnv, L, R>;
export function effectify<L, R>(f: Function): () => Effect<NoEnv, L, R>

Usage

Basic usage, detailed examples will be presented in the following sections

import { effect as T, exit as E } from "@matechs/effect";
import { right } from "fp-ts/lib/Either";
import { semigroupSum, fold } from "fp-ts/lib/Semigroup";

const pureValues = T.pure(1);

const syncValues = T.sync(() => 1);

const syncTry = T.trySync<Error>(() => {
  // tslint:disable-next-line: no-string-throw
  throw "error";
});

const asyncValue = T.async<never, number>(r => {
  const timer = setTimeout(() => {
    r(right(10));
  }, 100);

  return (cb) => {
    clearTimeout(timer);
    cb()
  };
});

const sumOf = fold(T.getSemigroup(semigroupSum))(T.pure(0), [
  pureValues,
  syncValues,
  asyncValue
]);

const foldExit = E.fold(
  a => console.log(a),
  e => console.error("error:", e),
  e => console.error("abort", e),
  () => console.error("interrupt")
);

T.run(pureValues, foldExit); // print 1
T.run(syncValues, foldExit); // print 1
T.run(syncTry, foldExit); // print error: error
T.run(asyncValue, foldExit); // print 10
T.run(sumOf, foldExit); // print 12

Last updated