/*! * Copyright (c) 2017-2018 by The Funfix Project Developers. * Some rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { HK, Monad } from "funland"; import { Either } from "funfix-core"; /** * Eval is a monad which controls evaluation. * * This type wraps a value (or an expression that produces a value) * and can produce it on command via the {@link Eval.get get()} * method. * * There are three basic evaluation strategies: * * - {@link Eval.now}: for describing strict values, evaluated * immediately * - {@link Eval.once}: evaluated only once when the value is needed, * with the result memoized (cached) for subsequent evaluations * - [[Eval.always]]: evaluated every time the value is needed, * being equivalent to a function * * Eval supports stack-safe lazy computation via the {@link Eval.map .map} * and {@link Eval.flatMap .flatMap} methods, which use an internal * trampoline to avoid stack overflows. Computation done within `map` * and `flatMap` is always done lazily, even when applied to an * `Eval.now` instance. * * Use `map` and `flatMap` to chain computation, and use `get()` to * get the result when needed. It is also not good style to create * `Eval` instances whose computation involves calling `get()` on * another `Eval` instance -- this can defeat the trampolining and * lead to stack overflows. * * ```typescript * const rndInt = Eval.of(() => { * const nr = Math.random() * 1000000 * return nr & nr * }) * * const evenInt = () => * rndInt.flatMap(int => { * if (i % 2 == 0) * return Eval.now(i) * else // Retry until we have an even number! * return evenInt() * }) * * const cached = evenInt().memoize() * * // Nothing happens until now, this triggers the * // actual evaluation: * const n: number = cached.get() * ``` * * ## Versus IO * * For dealing with lazy evaluation, the other alternative is * the {@link IO} data type. * * Differences between `Eval` and `IO`: * * 1. `IO` is capable of describing asynchronous computations as well * 2. `IO` is capable of error handling (it implements `MonadError`), * whereas `Eval` does not provide error handling capabilities, * being meant to be used for pure expressions (it implements * `Comonad`, which is incompatible with `MonadError`) * * So if you need error handling capabilities * (i.e. `MonadError`), or if you need to describe * asynchronous processes, then {@link IO} is for you. `Eval` * is a simpler data type with the sole purpose of controlling the * evaluation of expressions (i.e. strict versus lazy). * * ## Credits * * This type is inspired by `cats.Eval` from * {@link http://typelevel.org/cats/|Typelevel Cats} * and by `monix.eval.Coeval` from {@link https://monix.io|Monix}. * * @final */ export declare class Eval implements HK<"funfix/eval", A> { /** * Evaluates the source `Eval` and returns the result. * * ```typescript * const ref = Eval.always(() => 100 * 2) * * ref.get() // 200 * ``` */ get(): A; /** * Returns a new `Eval` that applies the mapping function to the * successful result emitted by the source. * * ```typescript * Eval.now(111).map(_ => _ * 2).get() // 222 * ``` */ map(f: (a: A) => B): Eval; /** * Creates a new `Eval` by applying a function to the successful * result of the source, and returns a new instance equivalent to * the result of the function. * * ```typescript * const rndInt = Eval.of(() => { * const nr = Math.random() * 1000000 * return nr & nr * }) * * const evenInt = () => * rndInt.flatMap(int => { * if (i % 2 == 0) * return Eval.now(i) * else // Retry until we have an even number! * return evenInt() * }) * ``` */ flatMap(f: (a: A) => Eval): Eval; /** Alias for {@link flatMap}. */ chain(f: (a: A) => Eval): Eval; /** * `Applicative` apply operator. * * Resembles {@link map}, but the passed mapping function is * lifted in the `Either` context. */ ap(ff: Eval<(a: A) => B>): Eval; /** * Memoizes (caches) the result of the source on the first * evaluation and reuses it on subsequent invocations of `get()`. * * The resulting `Eval` will be idempotent, meaning that * evaluating it multiple times will have the same effect * as evaluating it once. */ memoize(): Eval; /** * Returns a new `Eval` that upon evaluation will execute the given * function for the generated element, transforming the source into * an `Eval`. * * Similar in spirit with normal {@link Eval.forEach .forEach}, * but lazy, as obviously nothing gets executed at this point. */ forEachL(cb: (a: A) => void): Eval; /** * Triggers the evaluation of the source, executing the given * function for the generated element. * * The application of this function has strict behavior, as the * coeval is immediately executed. */ forEach(cb: (a: A) => void): void; /** * Identifies the `Eval` reference type, useful for debugging and * for pattern matching in the implementation. * * @hidden */ readonly _tag: "now" | "always" | "once" | "suspend" | "flatMap"; /** @hidden */ readonly _URI: "funfix/eval"; /** @hidden */ readonly _A: A; /** @hidden */ static readonly _Class: Eval; /** * Alias for {@link Eval.always}. */ static of(thunk: () => A): Eval; /** * Lifts a value into the `Eval` context. * * Alias for {@link Eval.now}. */ static pure(value: A): Eval; /** * Returns an `Eval` that on execution is always successful, * emitting the given strict value. */ static now(value: A): Eval; /** * Shorthand for `now(undefined as void)`, always returning * the same reference as optimization. */ static unit(): Eval; /** * Promote a `thunk` function to an `Eval`, catching exceptions in * the process. * * Note that since `Eval` is not memoized by global, this will * recompute the value each time the `Eval` is executed. */ static always(thunk: () => A): Eval; /** * Promote a `thunk` function to a `Coeval` that is memoized on the * first evaluation, the result being then available on subsequent * evaluations. * * Note this is equivalent with: * * ```typescript * Eval.always(thunk).memoize() * ``` */ static once(thunk: () => A): Eval; /** * Promote a `thunk` function generating `Eval` results to an `Eval` * of the same type. */ static suspend(thunk: () => Eval): Eval; /** * Promote a `thunk` function generating `Eval` results to an `Eval` * of the same type. * * Alias for {@link Eval.suspend}. */ static defer(thunk: () => Eval): Eval; /** * Keeps calling `f` until a `Right(b)` is returned. * * Based on Phil Freeman's * [Stack Safety for Free]{@link http://functorial.com/stack-safety-for-free/index.pdf}. * * Described in `FlatMap.tailRecM`. */ static tailRecM(a: A, f: (a: A) => Eval>): Eval; /** * Transforms a list of `Eval` values into an `Eval` of a list. * * Sample: * * ```typescript * const io1 = Eval.of(() => 1) * const io2 = Eval.of(() => 2) * const io3 = Eval.of(() => 3) * * // Yields [1, 2, 3] * const all: Eval = Eval.sequence([f1, f2, f3]) * ``` */ static sequence(list: Eval[] | Iterable>): Eval; /** * Maps 2 `Eval` values by the mapping function, returning a new * `Eval` reference that completes with the result of mapping that * function to the successful values of the futures, or in failure in * case either of them fails. * * ```typescript * const fa1 = Eval.of(() => 1) * const fa2 = Eval.of(() => 2) * * * // Yields Success(3) * Eval.map2(fa1, fa2, (a, b) => a + b) * ``` * * This operation is the `Applicative.map2`. */ static map2(fa1: Eval, fa2: Eval, f: (a1: A1, a2: A2) => R): Eval; /** * Maps 3 `Eval` values by the mapping function, returning a new * `Eval` reference that completes with the result of mapping that * function to the successful values of the futures, or in failure in * case either of them fails. * * ```typescript * const fa1 = Eval.of(() => 1) * const fa2 = Eval.of(() => 2) * const fa3 = Eval.of(() => 3) * * * // Yields Success(6) * Eval.map3(fa1, fa2, fa3, (a, b, c) => a + b + c) * ``` */ static map3(fa1: Eval, fa2: Eval, fa3: Eval, f: (a1: A1, a2: A2, a3: A3) => R): Eval; /** * Maps 4 `Eval` values by the mapping function, returning a new * `Eval` reference that completes with the result of mapping that * function to the successful values of the futures, or in failure in * case either of them fails. * * ```typescript * const fa1 = Eval.of(() => 1) * const fa2 = Eval.of(() => 2) * const fa3 = Eval.of(() => 3) * const fa4 = Eval.of(() => 4) * * // Yields Success(10) * Eval.map4(fa1, fa2, fa3, fa4, (a, b, c, d) => a + b + c + d) * ``` */ static map4(fa1: Eval, fa2: Eval, fa3: Eval, fa4: Eval, f: (a1: A1, a2: A2, a3: A3, a4: A4) => R): Eval; /** * Maps 5 `Eval` values by the mapping function, returning a new * `Eval` reference that completes with the result of mapping that * function to the successful values of the futures, or in failure in * case either of them fails. * * ```typescript * const fa1 = Eval.of(() => 1) * const fa2 = Eval.of(() => 2) * const fa3 = Eval.of(() => 3) * const fa4 = Eval.of(() => 4) * const fa5 = Eval.of(() => 5) * * // Yields Success(15) * Eval.map5(fa1, fa2, fa3, fa4, fa5, * (a, b, c, d, e) => a + b + c + d + e * ) * ``` */ static map5(fa1: Eval, fa2: Eval, fa3: Eval, fa4: Eval, fa5: Eval, f: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => R): Eval; /** * Maps 6 `Eval` values by the mapping function, returning a new * `Eval` reference that completes with the result of mapping that * function to the successful values of the futures, or in failure in * case either of them fails. * * ```typescript * const fa1 = Eval.of(() => 1) * const fa2 = Eval.of(() => 2) * const fa3 = Eval.of(() => 3) * const fa4 = Eval.of(() => 4) * const fa5 = Eval.of(() => 5) * const fa6 = Eval.of(() => 6) * * // Yields Success(21) * Eval.map6( * fa1, fa2, fa3, fa4, fa5, fa6, * (a, b, c, d, e, f) => a + b + c + d + e + f * ) * ``` */ static map6(fa1: Eval, fa2: Eval, fa3: Eval, fa4: Eval, fa5: Eval, fa6: Eval, f: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => R): Eval; } /** * Type enumerating the type classes implemented by `Eval`. */ export declare type EvalTypes = Monad<"funfix/eval">; /** * Type-class implementations, compatible with the `static-land` * specification. */ export declare const EvalModule: EvalTypes;