/*! * Copyright (c) 2017 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, Equiv, Constructor, getTypeClassInstance } from "./kinds" import { Functor, FunctorLaws } from "./functor" import { Either, Right, Left, applyMixins } from "funfix-core" /** * The `Apply` type class, a weaker version of {@link Applicative}, * exposing `ap` (apply), but not `pure`. * * This type class is exposed in addition to `Applicative` because * there are data types for which we can't implement `pure`, but * that could still benefit from an `ap` definition. For example * in case of a `Map` we couldn't define `pure` for it * because we don't have a `K` key. * * MUST obey the laws defined in {@link ApplyLaws}. * * Note that having an `Apply` instance implies that a * {@link Functor} implementation is also available, which is why * `Apply` is a subtype of `Functor`. * * ## Implementation notes * * Even though in TypeScript the Funfix library is using `abstract class` to * express type classes, when implementing this type class it is recommended * that you implement it as a mixin using "`implements`", instead of extending * it directly with "`extends`". See * [TypeScript: Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html} * for details and note that we already have `applyMixins` defined. * * Implementation example: * * ```typescript * import { * HK, Apply, * registerTypeClassInstance, * applyMixins * } from "funfix" * * // Type alias defined for readability. * // HK is our encoding for higher-kinded types. * type BoxK = HK, T> * * class Box implements HK, T> { * constructor(public value: T) {} * * // Implements HK, A>, not really needed, but useful in order * // to avoid type casts. Note these can and should be undefined: * readonly _funKindF: Box * readonly _funKindA: T * } * * class BoxApply implements Apply> { * map(fa: BoxK, f: (a: A) => B): Box { * const a = (fa as Box).value * return new Box(f(a)) * } * * ap(fa: BoxK, ff: BoxK<(a: A) => B>): Box { * const a = (fa as Box).value * const f = (ff as Box<(a: A) => B>).value * return new Box(f(a)) * } * * // Mixed-in, as these have default implementations * map2: (fa: BoxK, fb: BoxK, f: (a: A, b: B) => Z) => Box * product: (fa: BoxK, fb: BoxK) => Box<[A, B]> * } * * // Call needed in order to implement `map2` and `product` using * // the default implementations defined by `Apply`, because * // we are using `implements` instead of `extends` above and * // because in this sample we want the default implementations, * // but note that you can always provide your own definitions * applyMixins(BoxApply, [Apply]) * * // Registering global Apply instance for Box, needed in order * // for the `applyOf(Box)` calls to work * registerTypeClassInstance(Apply)(Box, new BoxApply()) * ``` * * We are using `implements` in order to support multiple inheritance and to * avoid inheriting any `static` members. In the Flow definitions (e.g. * `.js.flow` files) for Funfix these type classes are defined with * "`interface`", as they are meant to be interfaces that sometimes have * default implementations and not classes. * * ## Credits * * This type class is inspired by the equivalent in Haskell's * standard library and the implementation is inspired by the * [Typelevel Cats]{@link http://typelevel.org/cats/} project. */ export abstract class Apply implements Functor { /** * Given a value and a function in the `Apply` context, * applies the function to the value. */ abstract ap(fa: HK, ff: HK B>): HK /** Inherited from {@link Functor.map}. */ abstract map(fa: HK, f: (a: A) => B): HK /** * Applies the pure (binary) function `f` to the effectful values * `fa` and `fb`. * * `map2` can be seen as a binary version of {@link Functor.map}. */ map2(fa: HK, fb: HK, f: (a: A, b: B) => Z): HK { return this.ap(fb, this.map(fa, a => (b: B) => f(a, b))) } /** * Captures the idea of composing independent effectful values. * * It is of particular interest when taken together with [[Functor]]. * Where [[Functor]] captures the idea of applying a unary pure * function to an effectful value, calling `product` with `map` * allows one to apply a function of arbitrary arity to multiple * independent effectful values. * * This operation is equivalent with: * * ```typescript * map2(fa, fb, (a, b) => [a, b]) * ``` */ product(fa: HK, fb: HK): HK { return this.map2(fa, fb, (a: A, b: B) => [a, b] as [A, B]) } // Implements TypeClass /** @hidden */ static readonly _funTypeId: string = "apply" /** @hidden */ static readonly _funSupertypeIds: string[] = ["functor"] /** @hidden */ static readonly _funErasure: Apply } applyMixins(Apply, [Functor]) /** * Type class laws defined for {@link Apply}. * * This is an abstract definition. In order to use it in unit testing, * the implementor must think of a strategy to evaluate the truthiness * of the returned `Equiv` values. * * Even though in TypeScript the Funfix library is using classes to * express these laws, when implementing this class it is recommended * that you implement it as a mixin using `implements`, instead of extending * it directly with `extends`. See * [TypeScript: Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html} * for details and note that we already have `applyMixins` defined. * * We are doing this in order to support multiple inheritance and to * avoid inheriting any `static` members. In the Flow definitions (e.g. * `.js.flow` files) for Funfix these classes are defined with * `interface`, as they are meant to be interfaces that sometimes have * default implementations and not classes. */ export abstract class ApplyLaws implements FunctorLaws { /** * The {@link Apply} designated instance for `F`, * to be tested. */ public readonly F: Apply applyComposition(fa: HK, fab: HK B>, fbc: HK C>): Equiv> { const F = this.F const compose = (f: (b: B) => C) => ( (g: (a: A) => B) => (a: A) => f(g(a)) ) return Equiv.of( F.ap(F.ap(fa, fab), fbc), F.ap(fa, F.ap(fab, F.map(fbc, compose))) ) } applyProductConsistency(fa: HK, f: HK B>): Equiv> { const F = this.F return Equiv.of( F.ap(fa, f), F.map(F.product(f, fa), p => { const [f, a] = p; return f(a) }) ) } applyMap2Consistency(fa: HK, f: HK B>): Equiv> { const F = this.F return Equiv.of( F.ap(fa, f), F.map2(f, fa, (f, a) => f(a)) ) } /** Mixed-in from {@link FunctorLaws.covariantIdentity}. */ covariantIdentity: (fa: HK) => Equiv> /** Mixed-in from {@link FunctorLaws.covariantComposition}. */ covariantComposition: (fa: HK, f: (a: A) => B, g: (b: B) => C) => Equiv> } applyMixins(ApplyLaws, [FunctorLaws]) /** * Given a {@link Constructor} reference, returns its associated * {@link Apply} instance if it exists, or throws a `NotImplementedError` * in case there's no such association. * * ```typescript * import { Option, Apply, applyOf } from "funfix" * * const F: Apply> = applyOf(Option) * ``` */ export const applyOf: (c: Constructor) => Apply = getTypeClassInstance(Apply) /** * Given an {@link Apply} instance, returns the {@link ApplyLaws} * associated with it. */ export function applyLawsOf(instance: Apply): ApplyLaws { return new (class extends ApplyLaws { public readonly F = instance })() } /** * `Applicative` functor type class. * * Allows application of a function in an Applicative context to a * value in an `Applicative` context. * * References: * * - [The Essence of the Iterator Pattern]{@link https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf} * - [Applicative programming with effects]{@link http://staff.city.ac.uk/~ross/papers/Applicative.pdf} * * Example: * * ```typescript * const F = applicativeOf(Option) * * F.ap(F.pure(1), F.pure((x: number) => x + 1)) // Some(2) * ``` * * Note that having an `Applicative` instance implies * {@link Functor} and {@link Apply} implementations are also * available, which is why `Applicative` is a subtype of * `Functor` and `Apply`. * * ## Implementation notes * * Even though in TypeScript the Funfix library is using `abstract class` to * express type classes, when implementing this type class it is recommended * that you implement it as a mixin using "`implements`", instead of extending * it directly with "`extends`". See * [TypeScript: Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html} * for details and note that we already have `applyMixins` defined. * * Implementation example: * * ```typescript * import { * HK, Applicative, * registerTypeClassInstance, * applyMixins * } from "funfix" * * // Type alias defined for readability. * // HK is our encoding for higher-kinded types. * type BoxK = HK, T> * * class Box implements HK, T> { * constructor(public value: T) {} * * // Implements HK, A>, not really needed, but useful in order * // to avoid type casts. Note they can and should be undefined: * readonly _funKindF: Box * readonly _funKindA: T * } * * class BoxApplicative implements Applicative> { * pure(a: A): Box { return new Box(a) } * * ap(fa: BoxK, ff: BoxK<(a: A) => B>): Box { * const a = (fa as Box).value * const f = (ff as Box<(a: A) => B>).value * return new Box(f(a)) * } * * // Mixed-in, as these have default implementations * map: (fa: BoxK, f: (a: A) => B) => Box * map2: (fa: BoxK, fb: BoxK, f: (a: A, b: B) => Z) => Box * product: (fa: BoxK, fb: BoxK) => Box<[A, B]> * unit: () => Box * } * * // Call needed in order to implement `map`, `map2`, `product` and `unit`, * // using the default implementations defined by `Applicative`, because * // we are using `implements` instead of `extends` above and * // because in this sample we want the default implementations, * // but note that you can always provide your own * applyMixins(BoxApplicative, [Applicative]) * * // Registering global Applicative instance for Box, needed in order * // for the `functorOf(Box)`, `applyOf(Box)` and `applicativeOf(Box)` * // calls to work * registerTypeClassInstance(Applicative)(Box, new BoxApplicative()) * ``` * * We are using `implements` in order to support multiple inheritance and to * avoid inheriting any `static` members. In the Flow definitions (e.g. * `.js.flow` files) for Funfix these type classes are defined with * "`interface`", as they are meant to be interfaces that sometimes have * default implementations and not classes. * * ## Credits * * This type class is inspired by the equivalent in Haskell's * standard library and the implementation is inspired by the * [Typelevel Cats]{@link http://typelevel.org/cats/} project. */ export abstract class Applicative implements Apply { /** * Lifts a strict value `A` into the `F` context. */ abstract pure(a: A): HK /** Inherited from {@link Apply.ap}. */ abstract ap(fa: HK, ff: HK B>): HK /** * Shorthand for `pure(undefined)`, provided for convenience * and because implementations can override the default for * optimization purposes. */ unit(): HK { return this.pure(undefined) } /** Inherited from {@link Functor.map}. */ map(fa: HK, f: (a: A) => B): HK { return this.ap(fa, this.pure(f)) } /** Mixed-in from {@link Apply.map2}. */ map2: (fa: HK, fb: HK, f: (a: A, b: B) => Z) => HK /** Mixed-in from {@link Apply.product}. */ product: (fa: HK, fb: HK) => HK // Implements TypeClass /** @hidden */ static readonly _funTypeId: string = "applicative" /** @hidden */ static readonly _funSupertypeIds: string[] = ["functor", "apply"] /** @hidden */ static readonly _funErasure: Applicative } applyMixins(Applicative, [Apply]) /** * Type class laws defined for {@link Applicative}. * * This is an abstract definition. In order to use it in unit testing, * the implementor must think of a strategy to evaluate the truthiness * of the returned `Equiv` values. * * Even though in TypeScript the Funfix library is using classes to * express these laws, when implementing this class it is recommended * that you implement it as a mixin using `implements`, instead of extending * it directly with `extends`. See * [TypeScript: Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html} * for details and note that we already have `applyMixins` defined. * * We are doing this in order to support multiple inheritance and to * avoid inheriting any `static` members. In the Flow definitions (e.g. * `.js.flow` files) for Funfix these classes are defined with * `interface`, as they are meant to be interfaces that sometimes have * default implementations and not classes. */ export abstract class ApplicativeLaws implements ApplyLaws { /** * The {@link Applicative} designated instance for `F`, * to be tested. */ public readonly F: Applicative applicativeIdentity(fa: HK): Equiv> { const F = this.F return Equiv.of( F.ap(fa, F.pure((a: A) => a)), fa ) } applicativeHomomorphism(a: A, f: (a: A) => B): Equiv> { const F = this.F return Equiv.of( F.ap(F.pure(a), F.pure(f)), F.pure(f(a)) ) } applicativeInterchange(a: A, ff: HK B>): Equiv> { const F = this.F return Equiv.of( F.ap(F.pure(a), ff), F.ap(ff, F.pure((f: (a: A) => B) => f(a))) ) } applicativeMap(fa: HK, f: (a: A) => B): Equiv> { const F = this.F return Equiv.of( F.map(fa, f), F.ap(fa, F.pure(f)) ) } applicativeComposition(fa: HK, fab: HK B>, fbc: HK C>): Equiv> { const F = this.F const compose = (f: (b: B) => C) => ( (g: (a: A) => B) => (a: A) => f(g(a)) ) return Equiv.of( F.ap(fa, F.ap(fab, F.ap(fbc, F.pure(compose)))), F.ap(F.ap(fa, fab), fbc) ) } applicativeUnit(a: A): Equiv> { const F = this.F return Equiv.of(F.map(F.unit(), _ => a), F.pure(a)) } /** Mixed-in from {@link FunctorLaws.covariantIdentity}. */ covariantIdentity: (fa: HK) => Equiv> /** Mixed-in from {@link FunctorLaws.covariantComposition}. */ covariantComposition: (fa: HK, f: (a: A) => B, g: (b: B) => C) => Equiv> /** Mixed-in from {@link ApplyLaws.applyComposition}. */ applyComposition: (fa: HK, fab: HK B>, fbc: HK C>) => Equiv> /** Mixed-in from {@link ApplyLaws.applyProductConsistency}. */ applyProductConsistency: (fa: HK, f: HK B>) => Equiv> /** Mixed-in from {@link ApplyLaws.applyMap2Consistency}. */ applyMap2Consistency: (fa: HK, f: HK B>) => Equiv> } applyMixins(ApplicativeLaws, [ApplyLaws]) /** * Given a {@link Constructor} reference, returns its associated * {@link Applicative} instance if it exists, or throws a `NotImplementedError` * in case there's no such association. * * ```typescript * import { Option, Applicative, applicativeOf } from "funfix" * * const F: Applicative> = applicativeOf(Option) * ``` */ export const applicativeOf: (c: Constructor) => Applicative = getTypeClassInstance(Applicative) /** * Given an {@link Applicative} instance, returns the {@link ApplicativeLaws} * associated with it. */ export function applicativeLawsOf(instance: Applicative): ApplicativeLaws { return new (class extends ApplicativeLaws { public readonly F = instance })() } /** * The `ApplicativeError` type class is a {@link Applicative} that * also allows you to raise and or handle an error value. * * This type class allows one to abstract over error-handling * applicative types. * * MUST follow the law defined in {@link ApplicativeErrorLaws}. * * ## Implementation notes * * Even though in TypeScript the Funfix library is using `abstract class` to * express type classes, when implementing this type class it is recommended * that you implement it as a mixin using "`implements`", instead of extending * it directly with "`extends`". See * [TypeScript: Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html} * for details and note that we already have `applyMixins` defined. * * Implementation example: * * ```typescript * import { * HK, * ApplicativeError, * registerTypeClassInstance, * applyMixins, * Try * } from "funfix" * * // Type alias defined for readability. * // HK is our encoding for higher-kinded types. * type BoxK = HK, T> * * class Box implements HK, T> { * constructor(public value: Try) {} * * // Implements HK, A>, not really needed, but useful in order * // to avoid type casts. Note they can and should be undefined: * readonly _funKindF: Box * readonly _funKindA: T * } * * class BoxApplicativeError implements ApplicativeError, any> { * pure(a: A): Box { return new Box(Try.success(a)) } * * ap(fa: BoxK, ff: BoxK<(a: A) => B>): Box { * const ta = (fa as Box).value * const tf = (ff as Box<(a: A) => B>).value * return new Box(Try.map2(ta, tf, (a, f) => f(a))) * } * * raise(e: any): HK, A> { * return new Box(Try.failure(e)) * } * * recoverWith(fa: BoxK, f: (e: any) => BoxK): HK, A> { * return new Box((fa as Box).value.recoverWith(e => (f(e) as Box).value)) * } * * // Mixed-in, as these have default implementations * map: (fa: BoxK, f: (a: A) => B) => Box * map2: (fa: BoxK, fb: BoxK, f: (a: A, b: B) => Z) => Box * product: (fa: BoxK, fb: BoxK) => Box<[A, B]> * unit: () => Box * recover: (fa: HK, A>, f: (e: any) => A) => HK, A> * attempt: (fa: HK, A>) => HK, Either> * } * * // Call needed in order to implement `map`, `map2`, `product`, etc. * // using the default implementations defined by `ApplicativeError`, * // because we are using `implements` instead of `extends` above and * // because in this sample we want the default implementations, * // but note that you can always provide your own * applyMixins(BoxApplicativeError, [ApplicativeError]) * * // Registering global ApplicativeError instance for Box, needed in order * // for the `functorOf(Box)`, `applyOf(Box)`, `applicativeOf(Box)` * // and `applicativeErrorOf(Box)` calls to work * registerTypeClassInstance(ApplicativeError)(Box, new BoxApplicativeError()) * ``` * * We are using `implements` in order to support multiple inheritance and to * avoid inheriting any `static` members. In the Flow definitions (e.g. * `.js.flow` files) for Funfix these type classes are defined with * "`interface`", as they are meant to be interfaces that sometimes have * default implementations and not classes. * * ## Credits * * This type class is inspired by the equivalent in Haskell's * standard library and the implementation is inspired by the * [Typelevel Cats]{@link http://typelevel.org/cats/} project. */ export abstract class ApplicativeError implements Applicative { /** * Lift an error into the `F` context. */ abstract raise(e: E): HK /** * Handle any error, potentially recovering from it, by mapping it to an * `F` value. * * @see {@link recover} to handle any error by simply mapping it to an `A` * value instead of an `F`. */ abstract recoverWith(fa: HK, f: (e: E) => HK): HK /** * Handle any error by mapping it to an `A` value. * * @see {@link recoverWith} to map to an `F[A]` value instead of * simply an `A` value. */ recover(fa: HK, f: (e: E) => A): HK { const F = this return F.recoverWith(fa, e => F.pure(f(e))) } /** * Handle errors by turning them into `Either` values. * * If there is no error, then a `Right` value will be returned. * All non-fatal errors should be handled by this method. */ attempt(fa: HK): HK> { const F = this return F.recover( F.map(fa, a => Either.right(a)), Left) } /** Inherited from {@link Applicative.pure}. */ abstract pure(a: A): HK /** Inherited from {@link Applicative.ap}. */ abstract ap(fa: HK, ff: HK B>): HK /** Mixed-in from {@link Applicative.unit}. */ unit: () => HK /** Mixed-in from {@link Applicative.map}. */ map: (fa: HK, f: (a: A) => B) => HK /** Mixed-in from {@link Apply.map2}. */ map2: (fa: HK, fb: HK, f: (a: A, b: B) => Z) => HK /** Mixed-in from {@link Apply.product}. */ product: (fa: HK, fb: HK) => HK // Implements TypeClass /** @hidden */ static readonly _funTypeId: string = "applicativeError" /** @hidden */ static readonly _funSupertypeIds: string[] = ["functor", "apply", "applicative"] /** @hidden */ static readonly _funErasure: ApplicativeError } applyMixins(ApplicativeError, [Applicative]) /** * Type class laws defined for {@link ApplicativeError}. * * This is an abstract definition. In order to use it in unit testing, * the implementor must think of a strategy to evaluate the truthiness * of the returned `Equiv` values. * * Even though in TypeScript the Funfix library is using classes to * express these laws, when implementing this class it is recommended * that you implement it as a mixin using `implements`, instead of extending * it directly with `extends`. See * [TypeScript: Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html} * for details and note that we already have `applyMixins` defined. * * We are doing this in order to support multiple inheritance and to * avoid inheriting any `static` members. In the Flow definitions (e.g. * `.js.flow` files) for Funfix these classes are defined with * `interface`, as they are meant to be interfaces that sometimes have * default implementations and not classes. */ export abstract class ApplicativeErrorLaws implements ApplicativeLaws { /** * The {@link Applicative} designated instance for `F`, * to be tested. */ public readonly F: ApplicativeError applicativeErrorRecoverWith(e: E, f: (e: E) => HK): Equiv> { const F = this.F return Equiv.of(F.recoverWith(F.raise(e), f), f(e)) } applicativeErrorRecover(e: E, f: (e: E) => A): Equiv> { const F = this.F return Equiv.of(F.recover(F.raise(e), f), F.pure(f(e))) } recoverWithPure(a: A, f: (e: E) => HK): Equiv> { const F = this.F return Equiv.of(F.recoverWith(F.pure(a), f), F.pure(a)) } recoverPure(a: A, f: (e: E) => A): Equiv> { const F = this.F return Equiv.of(F.recover(F.pure(a), f), F.pure(a)) } raiseErrorAttempt(e: E): Equiv>> { const F = this.F return Equiv.of(F.attempt(F.raise(e)), F.pure(Left(e))) } pureAttempt(a: A): Equiv>> { const F = this.F return Equiv.of(F.attempt(F.pure(a)), F.pure(Right(a))) } /** Mixed-in from {@link ApplicativeLaws.applicativeIdentity}. */ applicativeIdentity: (fa: HK) => Equiv> /** Mixed-in from {@link ApplicativeLaws.applicativeHomomorphism}. */ applicativeHomomorphism: (a: A, f: (a: A) => B) => Equiv> /** Mixed-in from {@link ApplicativeLaws.applicativeInterchange}. */ applicativeInterchange: (a: A, ff: HK B>) => Equiv> /** Mixed-in from {@link ApplicativeLaws.applicativeMap}. */ applicativeMap: (fa: HK, f: (a: A) => B) => Equiv> /** Mixed-in from {@link ApplicativeLaws.applicativeComposition}. */ applicativeComposition: (fa: HK, fab: HK B>, fbc: HK C>) => Equiv> /** Mixed-in from {@link ApplicativeLaws.applicativeUnit}. */ applicativeUnit: (a: A) => Equiv> /** Mixed-in from {@link FunctorLaws.covariantIdentity}. */ covariantIdentity: (fa: HK) => Equiv> /** Mixed-in from {@link FunctorLaws.covariantComposition}. */ covariantComposition: (fa: HK, f: (a: A) => B, g: (b: B) => C) => Equiv> /** Mixed-in from {@link ApplyLaws.applyComposition}. */ applyComposition: (fa: HK, fab: HK B>, fbc: HK C>) => Equiv> /** Mixed-in from {@link ApplyLaws.applyProductConsistency}. */ applyProductConsistency: (fa: HK, f: HK B>) => Equiv> /** Mixed-in from {@link ApplyLaws.applyMap2Consistency}. */ applyMap2Consistency: (fa: HK, f: HK B>) => Equiv> } applyMixins(ApplicativeErrorLaws, [ApplicativeLaws]) /** * Given a {@link Constructor} reference, returns its associated * {@link ApplicativeError} instance if it exists, or throws a `NotImplementedError` * in case there's no such association. * * ```typescript * import { Eval, ApplicativeError, applicativeErrorOf } from "funfix" * * const F: ApplicativeError> = applicativeErrorOf(Eval) * ``` */ export const applicativeErrorOf: (c: Constructor) => ApplicativeError = getTypeClassInstance(ApplicativeError) /** * Given an {@link ApplicativeError} instance, returns the * {@link ApplicativeErrorLaws} associated with it. */ export function applicativeErrorLawsOf(instance: ApplicativeError): ApplicativeErrorLaws { return new (class extends ApplicativeErrorLaws { public readonly F = instance })() }