import type * as P from "@principia/prelude"; import * as HKT from "@principia/prelude/HKT"; import { bind_, flow, identity, pipe } from "../Function"; import * as X from "../XPure"; import { both_ } from "./applicative"; import { Functor, map, map_ } from "./functor"; import type { IO, URI, V } from "./model"; /* * ------------------------------------------- * Apply IO * ------------------------------------------- */ /** * ```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: IO<(a: A) => B>, fa: IO): IO => map_(X.both_(fab, fa), ([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: IO) => (fab: IO<(a: A) => B>): IO => 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: IO, fb: IO): IO => pipe( fa, map((a) => () => a), ap(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: IO) => (fa: IO): IO => 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: IO, fb: IO): IO => pipe( fa, map(() => (b: B) => b), ap(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: IO) => (fa: IO): IO => apSecond_(fa, fb); /** * ```haskell * mapBoth_ :: Apply f => (f a, f b, ((a, b) -> c)) -> f c * ``` * * Applies both `IO`s and maps their results with function `f` * * @category Apply * @since 1.0.0 */ export const mapBoth_: (fa: IO, fb: IO, f: (a: A, b: B) => C) => IO = X.mapBoth_; /** * ```haskell * mapBoth :: Apply f => (f b, ((a, b) -> c)) -> f a -> f c * ``` * * Applies both `IO`s and maps their results with function `f` * * @category Apply * @since 1.0.0 */ export const mapBoth = (fb: IO, f: (a: A, b: B) => C) => (fa: IO): IO => mapBoth_(fa, fb, f); /** * ```haskell * lift2 :: 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: IO) => (fb: IO): IO => map_(both_(fa, fb), ([a, b]) => f(a)(b)); /** * ```haskell * apS :: (Apply f, Nominal n) => * (n n3, f c) * -> f ({ n1: a, n2: b, ... }) * -> f ({ n1: a, n2: b, n3: c }) * ``` * * A pipeable version of `sequenceS` * * @category Apply * @since 1.0.0 */ export const apS = ( name: Exclude, fb: IO ): ((fa: IO) => IO<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }>) => flow( map((a) => (b: B) => bind_(a, name, b)), ap(fb) ); export const Apply: P.Apply<[URI], V> = HKT.instance({ ...Functor, ap_: ap_, ap, mapBoth_: mapBoth_, mapBoth });