import type * as P from "@principia/prelude";
import * as HKT from "@principia/prelude/HKT";
import { Functor, map_ } from "./functor";
import type { LazyPromise, URI, V } from "./model";
import { chain_ } from "./monad";
/*
* -------------------------------------------
* Apply LazyPromise
* -------------------------------------------
*/
/**
* ```haskell
* ap_ :: Apply f => (f (a -> b), f a) -> f b
* ```
*
* Apply a function to an argument under a type constructor
*
* @category Apply
* @since 1.0.0
*/
export const ap_ = (fab: LazyPromise<(a: A) => B>, fa: LazyPromise): LazyPromise => () =>
Promise.all([fab(), fa()]).then(([f, a]) => f(a));
/**
* ```haskell
* ap :: Apply f => f a -> f (a -> b) -> f b
* ```
*
* Apply a function to an argument under a type constructor
*
* @category Apply
* @since 1.0.0
*/
export const ap = (fa: LazyPromise) => (fab: LazyPromise<(a: A) => B>): LazyPromise => ap_(fab, fa);
/**
* ```haskell
* apFirst_ :: Apply f => (f a, f b) -> f a
* ```
*
* Combine two effectful actions, keeping only the result of the first
*
* @category Apply
* @since 1.0.0
*/
export const apFirst_ = (fa: LazyPromise, fb: LazyPromise): LazyPromise =>
ap_(
map_(fa, (a) => () => a),
fb
);
/**
* ```haskell
* apFirst :: Apply f => f b -> f a -> f a
* ```
*
* Combine two effectful actions, keeping only the result of the first
*
* @category Apply
* @since 1.0.0
*/
export const apFirst = (fb: LazyPromise) => (fa: LazyPromise): LazyPromise => apFirst_(fa, fb);
/**
* ```haskell
* apSecond_ :: Apply f => (f a, f b) -> f b
* ```
*
* Combine two effectful actions, keeping only the result of the second
*
* @category Apply
* @since 1.0.0
*/
export const apSecond_ = (fa: LazyPromise, fb: LazyPromise): LazyPromise =>
ap_(
map_(fa, () => (b: B) => b),
fb
);
/**
* ```haskell
* apSecond :: Apply f => f b -> f a -> f b
* ```
*
* Combine two effectful actions, keeping only the result of the second
*
* @category Apply
* @since 1.0.0
*/
export const apSecond = (fb: LazyPromise) => (fa: LazyPromise): LazyPromise => apSecond_(fa, fb);
/**
* ```haskell
* liftA2 :: Apply f => (a -> b -> c) -> f a -> f b -> f c
* ```
*
* Lifts a binary function to actions
*
* @category Apply
* @since 1.0.0
*/
export const liftA2 = (f: (a: A) => (b: B) => C) => (fa: LazyPromise) => (
fb: LazyPromise
): LazyPromise =>
ap_(
map_(fa, (a) => (b) => f(a)(b)),
fb
);
/**
* ```haskell
* mapBoth_ :: Apply f => (f a, f b, ((a, b) -> c)) -> f c
* ```
*
* Applies both `LazyPromise`s and maps their results with function `f`
*
* @category Apply
* @since 1.0.0
*/
export const mapBoth_ = (fa: LazyPromise, fb: LazyPromise, f: (a: A, b: B) => C): LazyPromise => () =>
Promise.all([fa(), fb()]).then(([a, b]) => f(a, b));
/**
* ```haskell
* mapBoth :: Apply f => (f b, ((a, b) -> c)) -> f a -> f c
* ```
*
* Applies both `LazyPromise`s and maps their results with function `f`
*
* @category Apply
* @since 1.0.0
*/
export const mapBoth = (fb: LazyPromise, f: (a: A, b: B) => C) => (fa: LazyPromise): LazyPromise =>
mapBoth_(fa, fb, f);
/**
* ```haskell
* apSeq_ :: Apply f => (f (a -> b), f a) -> f b
* ```
*
* Sequentially apply a function to an argument under a type constructor. For a parallel version, see `ap_`
*
* @category Apply
* @since 1.0.0
*/
export const apSeq_ = (fab: LazyPromise<(a: A) => B>, fa: LazyPromise): LazyPromise =>
chain_(fab, (f) => map_(fa, f));
/**
* ```haskell
* apSeq :: Apply f => f a -> f (a -> b) -> f b
* ```
*
* Sequentially apply a function to an argument under a type constructor. For a parallel version, see `ap`
*
* @category Apply
* @since 1.0.0
*/
export const apSeq = (fa: LazyPromise) => (fab: LazyPromise<(a: A) => B>): LazyPromise => apSeq_(fab, fa);
/**
* ```haskell
* apFirstSeq_ :: Apply f => (f a, f b) -> f a
* ```
*
* Sequentially combine two effectful actions, keeping only the result of the first. For a parallel version, see `apFirst_`
*
* @category Uncurried Apply
* @since 1.0.0
*/
export const apFirstSeq_ = (fa: LazyPromise, fb: LazyPromise): LazyPromise =>
apSeq_(
map_(fa, (a) => () => a),
fb
);
/**
* ```haskell
* apFirst :: Apply f => f b -> f a -> f a
* ```
*
* Sequentially combine two effectful actions, keeping only the result of the first. For a parallel version, see `apFirst`
*
* @category Apply
* @since 1.0.0
*/
export const apFirstSeq = (fb: LazyPromise) => (fa: LazyPromise): LazyPromise => apFirstSeq_(fa, fb);
/**
* ```haskell
* apSecondSeq_ :: Apply f => (f a, f b) -> f b
* ```
*
* Sequentially combine two effectful actions, keeping only the result of the second. For a parallel version, see `apSecond_`
*
* @category Apply
* @since 1.0.0
*/
export const apSecondSeq_ = (fa: LazyPromise, fb: LazyPromise): LazyPromise =>
apSeq_(
map_(fa, () => (b: B) => b),
fb
);
/**
* ```haskell
* apSecondSeq :: Apply f => f b -> f a -> f b
* ```
*
* Sequentially combine two effectful actions, keeping only the result of the second. For a parallel version, see `apSecond`
*
* @category Apply
* @since 1.0.0
*/
export const apSecondSeq = (fb: LazyPromise) => (fa: LazyPromise): LazyPromise => apSecondSeq_(fa, fb);
/**
* ```haskell
* mapBothSeq_ :: Apply f => (f a, f b, ((a, b) -> c)) -> f c
* ```
*
* Sequentially applies both `LazyPromise`s and maps their results with function `f`. For a parallel version, see `mapBoth_`
*
* @category Apply
* @since 1.0.0
*/
export const mapBothSeq_ = (fa: LazyPromise, fb: LazyPromise, f: (a: A, b: B) => C): LazyPromise =>
chain_(fa, (a) => map_(fb, (b) => f(a, b)));
/**
* ```haskell
* mapBoth :: Apply f => (f b, ((a, b) -> c)) -> f a -> f c
* ```
*
* Sequentially applies both `LazyPromise`s and maps their results with function `f`. For a parallel version, see `mapBoth`
*
* @category Apply
* @since 1.0.0
*/
export const mapBothSeq = (fb: LazyPromise, f: (a: A, b: B) => C) => (fa: LazyPromise): LazyPromise =>
mapBothSeq_(fa, fb, f);
/**
* ```haskell
* liftA2Seq :: Apply f => (a -> b -> c) -> f a -> f b -> f c
* ```
*
* Lifts a binary function to actions. `LazyPromise`s will be evaluated sequentially. For a parallel version, see `lift2`
*
* @category Apply
* @since 1.0.0
*/
export const liftA2Seq = (f: (a: A) => (b: B) => C) => (fa: LazyPromise) => (
fb: LazyPromise
): LazyPromise => chain_(fa, (a) => map_(fb, (b) => f(a)(b)));
export const ApplyPar: P.Apply<[URI], V> = HKT.instance({
...Functor,
ap_,
ap,
mapBoth_,
mapBoth
});
export const ApplySeq: P.Apply<[URI], V> = HKT.instance({
...Functor,
ap_: apSeq_,
ap: apSeq,
mapBoth_: mapBothSeq_,
mapBoth: mapBothSeq
});