/** * This file contains the Fn algebraic data type. Fn is short for a unary * function, which is to say a function that only takes one input variable. * Most computation can be encoded in Fn, but the standard ADT in most * functional languages is called Reader. * * @module Fn * @since 2.0.0 */ import "./_dnt.polyfills.js"; import type { In, Kind, Out } from "./kind.js"; import type { Applicable } from "./applicable.js"; import type { Composable } from "./composable.js"; import type { Flatmappable } from "./flatmappable.js"; import type { Mappable } from "./mappable.js"; import type { Premappable } from "./premappable.js"; import type { Wrappable } from "./wrappable.js"; /** * A Fn, also known as Reader or Environment, is a type over a unary * javascript function. ie. (a: number) => string can be * a Fn. As an algebraic data type, the associated type class instances * for Fn are limited to single variable inputs so they will look like * (a: number) => string, with only one argument. The purposes of a Fn * are many and varied, some common purposes are: computation, reading * values from a shared environment, and sub-computations in a modified * environment. In many ways Fn is a more powerful abstraction than * State, and indeed the State monad in fun is exactly State = * Fn<[S], [A, S]>. * * Currently, there is no implementation of Chain recursion or * trampolining for Fn implemented, but it is likely to be a future * feature. Once implemented Fn will gain some much needed stack safety. * * @since 2.0.0 */ export type Fn = (d: D) => A; /** * A Fn type over any, useful for constraining generics that * take or return Fns. * * @since 2.0.0 */ export type AnyFn = Fn; /** * Specifies Fn as a Higher Kinded Type, with * covariant parameter A corresponding to the 0th * index of any Substitutions and a contravariant * parameter D corresponding to the 0th index of * any Substititions. The Fn KindFn is unique in that * it constrains the Fn type to taking a single * argument for the purposes of type substitution * while the implementations of Fn combinators such * as map, flatmap, etc are mostly variadic (multiple * arguments). * * @since 2.0.0 */ export interface KindFn extends Kind { readonly kind: Fn, Out>; } /** * Take a variadic Fn and make it unary, collapsing multiple arguments * into a single tuple argument. * * @example * ```ts * import * as F from "./fn.ts"; * * const person = (age: number, name: string) => ({ age, name }); * const personUnary = F.unary(person); * * // ({ name: "Brandon", age: 37 }) * const result1 = personUnary([37, "Brandon"]); * ``` * @since 2.0.0 */ export declare function unary(fda: (...d: D) => A): Fn; /** * @since 2.0.0 */ export declare function curry2(fn: (a: A, b: B) => C): (a: A) => (b: B) => C; /** * @since 2.0.0 */ export declare function uncurry2(fn: (b: B) => (a: A) => C): (a: A, b: B) => C; /** * A common pattern in optics is to apply an input value to a function at the * beginning and the end of a computation. This can (and has) been achieved by * the composition of Pair using flow(P.dup, P.map(fn), P.merge). But for * performance reasons it's nice to have a straighforward function that achieves * the same result. * * @since 2.0.0 */ export declare function over(faai: (a: A) => (a: A) => I): (a: A) => I; /** * Wrap a thunk (a Fn that takes no arguments) in a try catch block, using * an onThrow function (that should itself never throw) to handle a default * case should the original function throw. This is useful for wrapping * functions that might throw. * * @example * ```ts * import * as F from "./fn.ts"; * * const getZero = (): number => { * if (Math.random() > 0.5) { * throw new Error("Too high!"); * } * return 0; * } * * const result = F.tryThunk(getZero, () => 0); // 0 * ``` * * @since 2.0.0 */ export declare function tryThunk(ua: Fn, onThrow: Fn): A; /** * Wrap any function in a try catch block, passing the result and * arguments to onResult when the function does not throw as well * as passing the error and arguments to the onThrow function when * it does. Neither the onResult nor the onThrow functions should * ever throw an error themselves. None of the functions in the fun * library will throw on their own. If they do it is a bug in fun * or the underlying runtime (or you used fun in javascript). This * function primarily exists to wrap functions exported from other * libraries that may use exceptions as their error mechanism. This * makes those functions safe to use with fun. * * @example * ```ts * import * as F from "./fn.ts"; * * const throwOverFive = (n: number): number => { * if (n > 5) { * throw new Error("Larger than five"); * } * return n; * } * const caught = F.handleThrow( * throwOverFive, * result => result, * err => 0, // Default to 0 * ); * * const result1 = caught(0); // 0 * const result2 = caught(5); // 5 * const result3 = caught(6); // 0 * ``` * * @since 2.0.0 */ export declare function handleThrow(ua: (...d: D) => A, onResult: (result: A, args: D) => I, onThrow: (error: unknown, args: D) => I): (...d: D) => I; /** * @since 2.0.0 */ export declare function tryCatch(fda: (...d: D) => A, onThrow: (e: unknown, d: D) => A): (...d: D) => A; /** * Memoize a unary function using a Map. This * means that this algorithm puposefully leaks memory. * * TODO: Extend memoize to be variadic. * * @example * ```ts * import * as F from "./fn.ts"; * * // Big old expensive recursive algorithm * const fib = (n: number): number => * n < 1 ? 0 : * n <= 1 ? 1 : * fib(n - 2) + fib(n - 1); * * const mfib = F.memoize(fib); * * const result1 = mfib(10); // 55 * const result2 = mfib(10); // 55 but does not recompute * ``` * * @since 2.0.0 */ export declare function memoize(ua: Fn): Fn; /** * A function that can be called to output any type. It's used for type * hole based programming. This allows one to define interfaces and types * for a function and stub them with todo() until you are ready to implement * the actual behavior. The todo function will throw if it is ever actually * called. * * @example * ```ts * import { todo } from "./fn.ts"; * * type InOut = { * read: () => Promise, * write: (s: string) => Promise, * } * * const mockInOut: InOut = todo(); // InOut !!THROWS!! * ``` * * @since 2.0.0 */ export declare function todo(): T; /** * Does an unsafe type coercion on any type. This is only safe when * the types A and I have referential transparency. This is to say * when the type A can be substituted for I and I for A at runtime * without there being any change to the operation of the program. * The primary use case for unsafeCoerce is in Newtype implementations. * * @since 2.0.0 */ export declare function unsafeCoerce(a: A): I; /** * The flow function is like the pipe function without the initial value. It * composes up to 9 functions from left to right (top to bottom). The first * function can take multiple arguments but every subsequent function must * be unary (only take one argument). * * @example * ```ts * import { flow } from "./fn.ts"; * * const add = (m: number) => (n: number) => m + n; * const multiply = (m: number) => (n: number) => m * n; * * const flowed = flow( * add(1), * multiply(2), * add(1), * multiply(2), * ); * * const result1 = flowed(1); // 10 * const result2 = flowed(2); // 14 * const result3 = flowed(3); // 18 * ``` * * @since 2.0.0 */ export declare function flow(ab: (...a: A) => B): (...a: A) => B; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C): (...a: A) => C; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...a: A) => D; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): (...a: A) => E; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): (...a: A) => F; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): (...a: A) => G; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H): (...a: A) => H; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I): (...a: A) => I; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J): (...a: A) => J; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K): (...a: A) => K; export declare function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (k: K) => L): (...a: A) => L; /** * The pipe takes a value as the first argument and composes it with subsequent * function arguments, returning the result of the last function passed in. It * handles and correctly types up to 10 unary functions. Beyond 10 it makes * sense to break up pipe into multiple pipes. * * @example * ```ts * import { pipe } from "./fn.ts"; * * const add = (n: number) => (m: number) => m + n; * const multiply = (n: number) => (m: number) => m * n; * * const result = pipe( * 1, * add(1), // 2 * multiply(2), // 4 * add(1), // 5 * multiply(2), // 10 * ); // 10 * ``` * * @since 2.0.0 */ export declare function pipe(a: A): A; export declare function pipe(a: A, ab: (a: A) => B): B; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C): C; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): G; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H): H; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I): I; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J): J; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K): K; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (K: K) => L): L; export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J, jk: (j: J) => K, kl: (K: K) => L, end: never): L; /** * Create a Fn that always returns a value. This is equivalent to * constant. * * @example * ```ts * import { wrap } from "./fn.ts"; * * const alwaysA = wrap("A"); * * const result = alwaysA(null); // "A" * ``` * * @since 2.0.0 */ export declare function wrap(a: A): Fn; /** * Create a Fn that always returns a value. This is equivalent to * of but without the ability to specify a contravariant argument. * * @example * ```ts * import { constant } from "./fn.ts"; * * const alwaysA = constant("A"); * * const result = alwaysA(); // "A" * ``` * * @since 2.0.0 */ export declare function constant(a: A): () => A; /** * Given L => A => I and D => A create a new Fn * D & L => I. In order to preserve type widening for * ap, it only handles unary functions. * * @example * ```ts * import * as F from "./fn.ts"; * * type Person = { name: string, age: number }; * * const person = (name: string) => (age: number): Person => ({ name, age }); * * const result = F.pipe( * F.wrap(person), * F.apply(F.wrap("Brandon")), * F.apply(F.wrap(37)), * ); // Fn<[], Person> * ``` * * @since 2.0.0 */ export declare function apply(ua: Fn): (ufai: Fn I>) => Fn; /** * Map over the output of a Fn. This is equivalent to * function composition. * ie. a => pipe(f, map(g))(a) === a => g(f(a)) * * @example * ```ts * import { map, wrap, pipe } from "./fn.ts"; * * const result = pipe(wrap(1), map(n => n + 1)); // 2 * ``` * * @since 2.0.0 */ export declare function map(fai: (a: A) => I): (ta: Fn) => Fn; /** * Create a new Fn by combining A => L => I with * D => A to produce D & L => I. This is equivalent * to ap with the first two arguments switched. It is * also limited to unary functions in order to properly * handle type widening on the input type. * * @example * ```ts * import { pipe, flatmap } from "./fn.ts"; * const add = (n: number) => (m: number) => n + m; * * const flatmaper = pipe( * (n: number) => n, * flatmap(add), * flatmap(add), * flatmap(add), * flatmap(add), * flatmap(add), * ); * * const result1 = flatmaper(1); // 6 * const result2 = flatmaper(2); // 12 * const result3 = flatmaper(3); // 18 * ``` * * @since 2.0.0 */ export declare function flatmap(fati: (a: A) => Fn): (ta: Fn) => Fn; /** * Map over the input of a function, turning * D => A and L => D into L => A. * * @example * ```ts * import { premap, pipe } from "./fn.ts"; * * const equalsZero = (n: number): boolean => n === 0; * const strLength = (s: string): number => s.length; * * const isEmpty = pipe( * equalsZero, * premap(strLength), * ); * * const result1 = isEmpty(""); // true * const result2 = isEmpty("Hello"); // false * ``` * * @since 2.0.0 */ export declare function premap(fld: (l: L) => D): (ta: Fn) => Fn; /** * A combination of premap and map, dimap applies fld * to the input of a function and fai to the output. * * @example * ```ts * import type { NonEmptyArray } from "./array.ts"; * import { dimap, pipe } from "./fn.ts"; * import { plural, split } from "./string.ts"; * * const are = plural("is", "are"); * const words = plural("word", "words"); * const describe = (n: number) => `There ${are(n)} ${n} ${words(n)}`; * * const toWords = split(/\s+/g); // string => string[] * const count = (ws: NonEmptyArray) => ws.length; * * const fromString = pipe( * count, * dimap(toWords, describe), * ); * * const result1 = fromString("Hello World"); // "There are 2 words" * const result2 = fromString("Hi"); // "There is 1 word" * const result3 = fromString("This is a test"); // "There are 4 words" * ``` * * @since 2.0.0 */ export declare function dimap(fld: (l: L) => D, fai: (a: A) => I): (ta: Fn) => Fn; /** * The canonical identity function. It returns whatever value was * passed to it. * * @example * ```ts * import { identity } from "./fn.ts"; * * const result1 = identity(1); // 1 * const result2 = identity("Hello"); // "Hello" * ``` * @since 2.0.0 */ export declare function identity(a: A): A; /** * A thunk over the identity function. It allows one * to constrain an identity to a specific type. * * @example * ```ts * import { id } from "./fn.ts"; * * const idString = id(); // (s: string) => string * const idNumber = id(); // (n: number) => number * * const result1 = idString("Hello"); // "Hello" * const result2 = idNumber(1); // 1 * ``` * * @since 2.0.0 */ export declare function id(): Fn; /** * Compose two functions by taking the output of * one and passing it to another. This is equivalent * to the map function. * * @example * ```ts * import { compose, pipe } from "./fn.ts"; * * const length = (s: string) => s.length; * const dup = (n: number) => n + n; * * const composed = pipe( * length, * compose(dup), * ); * * const result1 = composed("Hello"); // 10 * const result2 = composed(""); // 0 * ``` * * @since 2.0.0 */ export declare function compose(second: Fn): (first: Fn) => Fn; /** * The canonical implementation of Applicable for Fn. It contains * the methods of, ap, and map. * * @since 2.0.0 */ export declare const ApplicableFn: Applicable; /** * @since 2.0.0 */ export declare const ComposableFn: Composable; /** * The canonical implementation of Flatmappable for Fn. It contains * the methods of, ap, map, join, and flatmap. * * @since 2.0.0 */ export declare const FlatmappableFn: Flatmappable; /** * The canonical implementation of Mappable for Fn. It contains * the method map. * * @since 2.0.0 */ export declare const MappableFn: Mappable; /** * The canonical implementation of Premappable for Fn. It contains * the method premap. * * @since 2.0.0 */ export declare const PremappableFn: Premappable; /** * @since 2.0.0 */ export declare const WrappableFn: Wrappable; /** * @since 2.0.0 */ export declare const tap: (fn: (value: A) => void) => (ua: Fn) => Fn; /** * @since 2.0.0 */ export declare const bind: (name: Exclude, faui: (a: A) => Fn) => (ua: Fn) => Fn; /** * @since 2.0.0 */ export declare const bindTo: (name: N) => (ua: Fn) => Fn;