// @flow import type { HKT2 } from './HKT' import type { Monad } from './Monad' import type { Eff } from './Eff' import type { Either } from './Either' import type { Monoid } from './Monoid' import { HKT } from './HKT' import * as eff from './Eff' import * as either from './Either' class IsTask {} export type TaskV = () => Promise; // A Task is a computation which yields an effect E and a value A export type Task = HKT2; export type PureTask = Task<{}, A>; export type TaskF = HKT; export function inj(a: TaskV): Task { return ((a: any): Task) } export function prj(fa: Task): TaskV { return ((fa: any): TaskV) } export function runTask(fut: Task): Eff> { return eff.inj(prj(fut)) } export function unsafePerformTask(fut: Task): Promise { return prj(fut)() } export function map(f: (a: A) => B, fa: Task): Task { return inj(() => unsafePerformTask(fa).then(f)) } // the derived implementation // return chain(f => map(f, fa), fab) // will not execute the futures in parallel export function ap(fab: Task B>, fa: Task): Task { return inj(() => { return Promise.all([ unsafePerformTask(fab), unsafePerformTask(fa) ]).then(([f, a]) => f(a)) }) } export function of(a: A): Task { return inj(() => Promise.resolve(a)) } export function chain(f: (a: A) => Task, fa: Task): Task { return inj(() => unsafePerformTask(fa).then(a => unsafePerformTask(f(a)))) } const noop: () => void = () => {} // Returns a Task that will never resolve export function empty(): Task { return inj(() => new Promise(noop)) } // Selects the earlier of the two futures export function concat(x: Task, y: Task): Task { return inj(() => new Promise((resolve, reject) => { let done = false const guard = f => a => { if (!done) { done = true f(a) } } unsafePerformTask(x).then(guard(resolve), guard(reject)) unsafePerformTask(y).then(guard(resolve), guard(reject)) })) } // Returns a Task which yields `a` after `delay` milliseconds export function after(delay: number, a: A): Task { return inj(() => new Promise(resolve => { setTimeout(() => { resolve(a) }, delay) })) } // Catches a possible error returning it as an Either export function tryTask(fa: Task): Task> { return inj(() => unsafePerformTask(fa).then( either.right, either.left )) } if (false) { // eslint-disable-line ({ map, ap, of, chain, empty, concat }: Monad & Monoid>) }