/** * @since 1.0.0 */ import type { Either } from "@fp-ts/core/Either" import type { LazyArg } from "@fp-ts/core/Function" import { constNull, constUndefined, dual } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import { structural } from "@fp-ts/core/internal/effect" import * as either from "@fp-ts/core/internal/Either" import * as option from "@fp-ts/core/internal/Option" import * as N from "@fp-ts/core/Number" import type { Predicate, Refinement } from "@fp-ts/core/Predicate" import type * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as chainable from "@fp-ts/core/typeclass/Chainable" import type * as coproduct_ from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as filterable from "@fp-ts/core/typeclass/Filterable" import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" import type * as monad from "@fp-ts/core/typeclass/Monad" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" import type { Order } from "@fp-ts/core/typeclass/Order" import * as order from "@fp-ts/core/typeclass/Order" import type * as pointed from "@fp-ts/core/typeclass/Pointed" import * as product_ from "@fp-ts/core/typeclass/Product" import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" // ------------------------------------------------------------------------------------- // models // ------------------------------------------------------------------------------------- /** * @category models * @since 1.0.0 */ export interface None { readonly _tag: "None" } /** * @category models * @since 1.0.0 */ export interface Some { readonly _tag: "Some" readonly value: A } /** * @category models * @since 1.0.0 */ export type Option = None | Some /** * @category type lambdas * @since 1.0.0 */ export interface OptionTypeLambda extends TypeLambda { readonly type: Option } // ------------------------------------------------------------------------------------- // constructors // ------------------------------------------------------------------------------------- /** * Creates a new `Option` that represents the absence of a value. * * This can be useful when working with optional values or to represent a computation that failed. * It returns a new `Option` object that does not contain any value. * * @category constructors * @since 1.0.0 */ export const none = (): Option => option.none /** * Creates a new `Option` that wraps the given value. * * This can be useful when working with optional values or to represent a computation that succeeded with a value. * * @param value - The value to wrap. * * @category constructors * @since 1.0.0 */ export const some: (value: A) => Option = option.some /** * Alias of {@link some}. * * @category constructors * @since 1.0.0 */ export const of: (value: A) => Option = some // ------------------------------------------------------------------------------------- // guards // ------------------------------------------------------------------------------------- /** * Tests if a value is a `Option`. * * @param input - The value to check. * * @example * import { some, none, isOption } from '@fp-ts/core/Option' * * assert.deepStrictEqual(isOption(some(1)), true) * assert.deepStrictEqual(isOption(none()), true) * assert.deepStrictEqual(isOption({}), false) * * @category guards * @since 1.0.0 */ export const isOption = (input: unknown): input is Option => typeof input === "object" && input != null && structural in input && "_tag" in input && (input["_tag"] === "None" || input["_tag"] === "Some") /** * Determine if a `Option` is a `None`. * * @param self - The `Option` to check. * * @example * import { some, none, isNone } from '@fp-ts/core/Option' * * assert.deepStrictEqual(isNone(some(1)), false) * assert.deepStrictEqual(isNone(none()), true) * * @category guards * @since 1.0.0 */ export const isNone: (self: Option) => self is None = option.isNone /** * Determine if a `Option` is a `Some`. * * @param self - The `Option` to check. * * @example * import { some, none, isSome } from '@fp-ts/core/Option' * * assert.deepStrictEqual(isSome(some(1)), true) * assert.deepStrictEqual(isSome(none()), false) * * @category guards * @since 1.0.0 */ export const isSome: (self: Option) => self is Some = option.isSome // ------------------------------------------------------------------------------------- // pattern matching // ------------------------------------------------------------------------------------- /** * Matches the given `Option` and returns either the provided `onNone` value or the result of the provided `onSome` * function when passed the `Option`'s value. * * @param self - The `Option` to match * @param onNone - The value to be returned if the `Option` is `None` * @param onSome - The function to be called if the `Option` is `Some`, it will be passed the `Option`'s value and its result will be returned * * @example * import { some, none, match } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual( * pipe( * some(1), * match(() => 'a none', a => `a some containing ${a}`) * ), * 'a some containing 1' * ) * * assert.deepStrictEqual( * pipe( * none(), * match(() => 'a none', a => `a some containing ${a}`) * ), * 'a none' * ) * * @category pattern matching * @since 1.0.0 */ export const match: { (onNone: LazyArg, onSome: (a: A) => C): (self: Option) => B | C (self: Option, onNone: LazyArg, onSome: (a: A) => C): B | C } = dual( 3, (self: Option, onNone: LazyArg, onSome: (a: A) => C): B | C => isNone(self) ? onNone() : onSome(self.value) ) // ------------------------------------------------------------------------------------- // conversions // ------------------------------------------------------------------------------------- /** * Returns a `Refinement` from a `Option` returning function. * This function ensures that a `Refinement` definition is type-safe. * * @category conversions * @since 1.0.0 */ export const toRefinement = (f: (a: A) => Option): Refinement => (a: A): a is B => isSome(f(a)) /** * Converts an `Iterable` of values into an `Option`. Returns the first value of the `Iterable` wrapped in a `Some` * if the `Iterable` is not empty, otherwise returns `None`. * * @param collection - The `Iterable` to be converted to an `Option`. * * @example * import { fromIterable, some, none } from '@fp-ts/core/Option' * * const collection = [1, 2, 3] * assert.deepStrictEqual(fromIterable(collection), some(1)) * assert.deepStrictEqual(fromIterable([]), none()) * * @category conversions * @since 1.0.0 */ export const fromIterable = (collection: Iterable): Option => { for (const a of collection) { return some(a) } return none() } /** * Converts a `Either` to an `Option` discarding the error. * * @param self - The `Either` to convert to an `Option`. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(O.fromEither(E.right(1)), O.some(1)) * assert.deepStrictEqual(O.fromEither(E.left('a')), O.none()) * * @category conversions * @since 1.0.0 */ export const fromEither: (self: Either) => Option = either.getRight /** * Converts a `Either` to an `Option` discarding the error. * * Alias of {@link fromEither}. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(O.getRight(E.right('ok')), O.some('ok')) * assert.deepStrictEqual(O.getRight(E.left('err')), O.none()) * * @category conversions * @since 1.0.0 */ export const getRight: (self: Either) => Option = fromEither /** * Converts a `Either` to an `Option` discarding the value. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(O.getLeft(E.right('ok')), O.none()) * assert.deepStrictEqual(O.getLeft(E.left('err')), O.some('err')) * * @category conversions * @since 1.0.0 */ export const getLeft: (self: Either) => Option = either.getLeft /** * Converts an `Option` to an `Either`, allowing you to provide a value to be used in the case of a `None`. * * @param self - the `Option` to convert. * @param onNone - a function that produces an error value when the `Option` is `None`. * * @example * import { pipe } from '@fp-ts/core/Function' * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * const onNone = () => 'error' * assert.deepStrictEqual(pipe(O.some(1), O.toEither(onNone)), E.right(1)) * assert.deepStrictEqual(pipe(O.none(), O.toEither(onNone)), E.left('error')) * * @category conversions * @since 1.0.0 */ export const toEither: { (self: Option, onNone: () => E): Either (onNone: () => E): (self: Option) => Either } = either.fromOption // ------------------------------------------------------------------------------------- // error handling // ------------------------------------------------------------------------------------- /** * Returns the value of the `Option` if it is `Some`, otherwise returns `onNone` * * @param self - The `Option` to get the value of. * @param onNone - Function that returns the default value to return if the `Option` is `None`. * * @example * import { some, none, getOrElse } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(some(1), getOrElse(() => 0)), 1) * assert.deepStrictEqual(pipe(none(), getOrElse(() => 0)), 0) * * @category error handling * @since 1.0.0 */ export const getOrElse: { (onNone: LazyArg): (self: Option) => B | A (self: Option, onNone: LazyArg): A | B } = dual( 2, (self: Option, onNone: LazyArg): A | B => isNone(self) ? onNone() : self.value ) /** * Returns the provided `Option` `that` if `self` is `None`, otherwise returns `self`. * * @param self - The first `Option` to be checked. * @param that - The `Option` to return if `self` is `None`. * * @example * import * as O from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual( * pipe( * O.none(), * O.orElse(() => O.none()) * ), * O.none() * ) * assert.deepStrictEqual( * pipe( * O.some('a'), * O.orElse(() => O.none()) * ), * O.some('a') * ) * assert.deepStrictEqual( * pipe( * O.none(), * O.orElse(() => O.some('b')) * ), * O.some('b') * ) * assert.deepStrictEqual( * pipe( * O.some('a'), * O.orElse(() => O.some('b')) * ), * O.some('a') * ) * * @category error handling * @since 1.0.0 */ export const orElse: { (that: LazyArg>): (self: Option) => Option (self: Option, that: LazyArg>): Option } = dual( 2, (self: Option, that: LazyArg>): Option => isNone(self) ? that() : self ) /** * Similar to `orElse`, but instead of returning a simple union, it returns an `Either` object, * which contains information about which of the two `Option`s has been chosen. * * This is useful when it's important to know whether the value was retrieved from the first `Option` or the second option. * * @param self - The first `Option` to be checked. * @param that - The second `Option` to be considered if the first `Option` is `None`. * * @category error handling * @since 1.0.0 */ export const orElseEither: { (that: LazyArg>): (self: Option) => Option> (self: Option, that: LazyArg>): Option> } = dual( 2, (self: Option, that: LazyArg>): Option> => isNone(self) ? map(that(), either.right) : map(self, either.left) ) /** * Given an `Iterable` collection of `Option`s, the function returns the first `Some` found in the collection. * * @param collection - An iterable collection of `Option` to be searched. * * @category error handling * @since 1.0.0 */ export const firstSomeOf = (collection: Iterable>): Option => { let out: Option = none() for (out of collection) { if (isSome(out)) { return out } } return out } // ------------------------------------------------------------------------------------- // interop // ------------------------------------------------------------------------------------- /** * Constructs a new `Option` from a nullable type. If the value is `null` or `undefined`, returns `None`, otherwise * returns the value wrapped in a `Some`. * * @param nullableValue - The nullable value to be converted to an `Option`. * * @example * import { none, some, fromNullable } from '@fp-ts/core/Option' * * assert.deepStrictEqual(fromNullable(undefined), none()) * assert.deepStrictEqual(fromNullable(null), none()) * assert.deepStrictEqual(fromNullable(1), some(1)) * * @category interop * @since 1.0.0 */ export const fromNullable = ( nullableValue: A ): Option< NonNullable > => (nullableValue == null ? none() : some(nullableValue as NonNullable)) /** * This API is useful for lifting a function that returns `null` or `undefined` into the `Option` context. * * @example * import { liftNullable, none, some } from '@fp-ts/core/Option' * * const parse = (s: string): number | undefined => { * const n = parseFloat(s) * return isNaN(n) ? undefined : n * } * * const parseOption = liftNullable(parse) * * assert.deepStrictEqual(parseOption('1'), some(1)) * assert.deepStrictEqual(parseOption('not a number'), none()) * * @category interop * @since 1.0.0 */ export const liftNullable = , B>( f: (...a: A) => B | null | undefined ): ((...a: A) => Option>) => (...a) => fromNullable(f(...a)) /** * Returns the value of the `Option` if it is a `Some`, otherwise returns `null`. * * @param self - The `Option` to extract the value from. * * @example * import { some, none, getOrNull } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(some(1), getOrNull), 1) * assert.deepStrictEqual(pipe(none(), getOrNull), null) * * @category interop * @since 1.0.0 */ export const getOrNull: (self: Option) => A | null = getOrElse(constNull) /** * Returns the value of the `Option` if it is a `Some`, otherwise returns `undefined`. * * @param self - The `Option` to extract the value from. * * @example * import { some, none, getOrUndefined } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(some(1), getOrUndefined), 1) * assert.deepStrictEqual(pipe(none(), getOrUndefined), undefined) * * @category interop * @since 1.0.0 */ export const getOrUndefined: (self: Option) => A | undefined = getOrElse(constUndefined) /** * A utility function that lifts a function that throws exceptions into a function that returns an `Option`. * * This function is useful for any function that might throw an exception, allowing the developer to handle * the exception in a more functional way. * * @param f - the function that can throw exceptions. * * @example * import { liftThrowable, some, none } from "@fp-ts/core/Option"; * * const parse = liftThrowable(JSON.parse) * * assert.deepStrictEqual(parse("1"), some(1)) * assert.deepStrictEqual(parse(""), none()) * * @category interop * @since 1.0.0 */ export const liftThrowable = , B>( f: (...a: A) => B ): ((...a: A) => Option) => (...a) => { try { return some(f(...a)) } catch (e) { return none() } } /** * Extracts the value of an `Option` or throws if the `Option` is `None`. * * If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. * * @param self - The `Option` to extract the value from. * @param onNone - A function that will be called if the `Option` is `None`. It returns the error to be thrown. * * @example * import * as O from '@fp-ts/core/Option' * * assert.deepStrictEqual( * O.getOrThrowWith(O.some(1), () => new Error('Unexpected None')), * 1 * ) * assert.throws(() => O.getOrThrowWith(O.none(), () => new Error('Unexpected None'))) * * @category interop * @since 1.0.0 */ export const getOrThrowWith: { (onNone: () => unknown): (self: Option) => A (self: Option, onNone: () => unknown): A } = dual(2, (self: Option, onNone: () => unknown): A => { if (isSome(self)) { return self.value } throw onNone() }) /** * Extracts the value of an `Option` or throws if the `Option` is `None`. * * The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. * * @param self - The `Option` to extract the value from. * @throws `Error("getOrThrow called on a None")` * * @example * import * as O from '@fp-ts/core/Option' * * assert.deepStrictEqual(O.getOrThrow(O.some(1)), 1) * assert.throws(() => O.getOrThrow(O.none())) * * @category interop * @since 1.0.0 */ export const getOrThrow: (self: Option) => A = getOrThrowWith(() => new Error("getOrThrow called on a None") ) // ------------------------------------------------------------------------------------- // mapping // ------------------------------------------------------------------------------------- /** * Maps the `Some` side of an `Option` value to a new `Option` value. * * @param self - An `Option` to map * @param f - The function to map over the value of the `Option` * * @category mapping * @since 1.0.0 */ export const map: { (f: (a: A) => B): (self: Option) => Option (self: Option, f: (a: A) => B): Option } = dual( 2, (self: Option, f: (a: A) => B): Option => isNone(self) ? none() : some(f(self.value)) ) const imap = covariant.imap(map) /** * @category mapping * @since 1.0.0 */ export const Covariant: covariant.Covariant = { imap, map } /** * @category mapping * @since 1.0.0 */ export const Invariant: invariant.Invariant = { imap } /** * @category mapping * @since 1.0.0 */ export const tupled: (self: Option) => Option<[A]> = invariant.tupled(Invariant) /** * @category mapping * @since 1.0.0 */ export const flap: { (a: A, self: Option<(a: A) => B>): Option (self: Option<(a: A) => B>): (a: A) => Option } = covariant.flap(Covariant) /** * Maps the `Some` value of this `Option` to the specified constant value. * * @category mapping * @since 1.0.0 */ export const as: { <_, B>(self: Option<_>, b: B): Option (b: B): <_>(self: Option<_>) => Option } = covariant.as(Covariant) /** * Returns the `Option` resulting from mapping the `Some` value to `void`. * * This is useful when the value of the `Option` is not needed, but the presence or absence of the value is important. * * @category mapping * @since 1.0.0 */ export const asUnit: <_>(self: Option<_>) => Option = covariant.asUnit(Covariant) /** * @since 1.0.0 */ export const Of: of_.Of = { of } /** * @since 1.0.0 */ export const unit: Option = of_.unit(Of) /** * @since 1.0.0 */ export const Pointed: pointed.Pointed = { of, imap, map } // ------------------------------------------------------------------------------------- // sequencing // ------------------------------------------------------------------------------------- /** * Applies a function to the value of an `Option` and flattens the result, if the input is `Some`. * * @category sequencing * @since 1.0.0 */ export const flatMap: { (f: (a: A) => Option): (self: Option) => Option (self: Option, f: (a: A) => Option): Option } = dual( 2, (self: Option, f: (a: A) => Option): Option => isNone(self) ? none() : f(self.value) ) /** * Applies a provided function that returns an `Either` to the contents of an `Option`, flattening the result into another `Option`. * * @param self - The `Option` to apply the function to. * @param f - The function to be applied to the contents of the `Option`. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * import { pipe } from '@fp-ts/core/Function' * * const f = (n: number) => (n > 2 ? E.left('Too big') : E.right(n + 1)) * * assert.deepStrictEqual(pipe(O.some(1), O.flatMapEither(f)), O.some(2)) * assert.deepStrictEqual(pipe(O.some(3), O.flatMapEither(f)), O.none()) * * @category sequencing * @since 1.0.0 */ export const flatMapEither: { (f: (a: A) => Either): (self: Option) => Option (self: Option, f: (a: A) => Either): Option } = dual( 2, (self: Option, f: (a: A) => Either): Option => flatMap(self, liftEither(f)) ) /** * This is `flatMap` + `fromNullable`, useful when working with optional values. * * @example * import { some, none, flatMapNullable } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * interface Employee { * company?: { * address?: { * street?: { * name?: string * } * } * } * } * * const employee1: Employee = { company: { address: { street: { name: 'high street' } } } } * * assert.deepStrictEqual( * pipe( * some(employee1), * flatMapNullable(employee => employee.company?.address?.street?.name), * ), * some('high street') * ) * * const employee2: Employee = { company: { address: { street: {} } } } * * assert.deepStrictEqual( * pipe( * some(employee2), * flatMapNullable(employee => employee.company?.address?.street?.name), * ), * none() * ) * * @category sequencing * @since 1.0.0 */ export const flatMapNullable: { (f: (a: A) => B | null | undefined): (self: Option) => Option> (self: Option, f: (a: A) => B | null | undefined): Option> } = dual( 2, (self: Option, f: (a: A) => B | null | undefined): Option> => isNone(self) ? none() : fromNullable(f(self.value)) ) /** * @category sequencing * @since 1.0.0 */ export const FlatMap: flatMap_.FlatMap = { flatMap } /** * @category sequencing * @since 1.0.0 */ export const flatten: (self: Option>) => Option = flatMap_.flatten(FlatMap) /** * @category sequencing * @since 1.0.0 */ export const andThen: { <_, B>(self: Option<_>, that: Option): Option (that: Option): <_>(self: Option<_>) => Option } = flatMap_.andThen(FlatMap) /** * @category sequencing * @since 1.0.0 */ export const composeKleisliArrow: { (afb: (a: A) => Option, bfc: (b: B) => Option): (a: A) => Option (bfc: (b: B) => Option): (afb: (a: A) => Option) => (a: A) => Option } = flatMap_.composeKleisliArrow(FlatMap) /** * @category sequencing * @since 1.0.0 */ export const Chainable: chainable.Chainable = { imap, map, flatMap } /** * Sequences the specified `that` `Option` but ignores its value. * * It is useful when we want to chain multiple operations, but only care about the result of `self`. * * @param that - The `Option` that will be ignored in the chain and discarded * @param self - The `Option` we care about * * @category sequencing * @since 1.0.0 */ export const andThenDiscard: { (self: Option, that: Option<_>): Option <_>(that: Option<_>): (self: Option) => Option } = chainable.andThenDiscard(Chainable) /** * Applies the provided function `f` to the value of the `Option` if it is `Some` and returns the original `Option` * unless `f` returns `None`, in which case it returns `None`. * * This function is useful for performing additional computations on the value of the input `Option` without affecting its value. * * @param f - Function to apply to the value of the `Option` if it is `Some` * @param self - The `Option` to apply the function to * * @category sequencing * @since 1.0.0 */ export const tap: { (self: Option, f: (a: A) => Option<_>): Option (f: (a: A) => Option<_>): (self: Option) => Option } = chainable.tap(Chainable) // ------------------------------------------------------------------------------------- // debugging // ------------------------------------------------------------------------------------- /** * Useful for debugging purposes, the `onSome` callback is called with the value of `self` if it is a `Some`. * * @param self - the `Option` to inspect * @param onSome - callback function that is called with the value of `self` if it is a `Some` * * @category debugging * @since 1.0.0 */ export const inspectSome: { (onSome: (a: A) => void): (self: Option) => Option (self: Option, onSome: (a: A) => void): Option } = dual(2, (self: Option, onSome: (a: A) => void): Option => { if (isSome(self)) { onSome(self.value) } return self }) /** * Useful for debugging purposes, the `onNone` callback is is called if `self` is a `None`. * * @param self - the `Option` to inspect * @param onNone - callback function that is is called if `self` is a `None` * * @category debugging * @since 1.0.0 */ export const inspectNone: { (onNone: () => void): (self: Option) => Option (self: Option, onNone: () => void): Option } = dual(2, (self: Option, onNone: () => void): Option => { if (isNone(self)) { onNone() } return self }) /** * @category instances * @since 1.0.0 */ export const Monad: monad.Monad = { imap, of, map, flatMap } const product = (self: Option, that: Option): Option<[A, B]> => isSome(self) && isSome(that) ? some([self.value, that.value]) : none() const productMany = ( self: Option, collection: Iterable> ): Option<[A, ...Array]> => { if (isNone(self)) { return none() } const out: [A, ...Array] = [self.value] for (const o of collection) { if (isNone(o)) { return none() } out.push(o.value) } return some(out) } /** * @category instances * @since 1.0.0 */ export const SemiProduct: semiProduct.SemiProduct = { imap, product, productMany } /** * Appends an element to the end of a tuple. * * @since 1.0.0 */ export const appendElement: { , B>(self: Option, that: Option): Option<[...A, B]> (that: Option): >(self: Option) => Option<[...A, B]> } = semiProduct.appendElement(SemiProduct) const productAll = (collection: Iterable>): Option> => { const out: Array = [] for (const o of collection) { if (isNone(o)) { return none() } out.push(o.value) } return some(out) } /** * @category instances * @since 1.0.0 */ export const Product: product_.Product = { of, imap, product, productMany, productAll } /** * @since 1.0.0 */ export const tuple: >>( ...tuple: T ) => Option<{ [I in keyof T]: [T[I]] extends [Option] ? A : never }> = product_.tuple( Product ) /** * @since 1.0.0 */ export const struct: >>( r: R ) => Option<{ [K in keyof R]: [R[K]] extends [Option] ? A : never }> = product_.struct( Product ) /** * @category instances * @since 1.0.0 */ export const SemiApplicative: semiApplicative.SemiApplicative = { imap, map, product, productMany } /** * Monoid that models the combination of values that may be absent, elements that are `None` are ignored * while elements that are `Some` are combined using the provided `Semigroup`. * * @example * import { getOptionalMonoid, some, none } from '@fp-ts/core/Option' * import * as N from '@fp-ts/core/Number' * import { pipe } from '@fp-ts/core/Function' * * const M = getOptionalMonoid(N.SemigroupSum) * assert.deepStrictEqual(M.combine(none(), none()), none()) * assert.deepStrictEqual(M.combine(some(1), none()), some(1)) * assert.deepStrictEqual(M.combine(none(), some(1)), some(1)) * assert.deepStrictEqual(M.combine(some(1), some(2)), some(3)) * * @category instances * @since 1.0.0 */ export const getOptionalMonoid = ( Semigroup: Semigroup ): Monoid> => monoid.fromSemigroup( semigroup.make((self, that) => isNone(self) ? that : isNone(that) ? self : some(Semigroup.combine(self.value, that.value)) ), none() ) // ------------------------------------------------------------------------------------- // combining // ------------------------------------------------------------------------------------- /** * Zips two `Option` values together using a provided function, returning a new `Option` of the result. * * @param self - The left-hand side of the zip operation * @param that - The right-hand side of the zip operation * @param f - The function used to combine the values of the two `Option`s * * @category combining * @since 1.0.0 */ export const zipWith: { (self: Option, that: Option, f: (a: A, b: B) => C): Option (that: Option, f: (a: A, b: B) => C): (self: Option) => Option } = semiApplicative.zipWith(SemiApplicative) /** * @category combining * @since 1.0.0 */ export const ap: { (self: Option<(a: A) => B>, that: Option): Option (that: Option): (self: Option<(a: A) => B>) => Option } = semiApplicative.ap(SemiApplicative) /** * Semigroup that models the combination of computations that can fail, if at least one element is `None` * then the resulting combination is `None`, otherwise if all elements are `Some` then the resulting combination * is the combination of the wrapped elements using the provided `Semigroup`. * * See also `getFailureMonoid` if you need a `Monoid` instead of a `Semigroup`. * * @category combining * @since 1.0.0 */ export const getFailureSemigroup: (S: Semigroup) => Semigroup> = semiApplicative .getSemigroup(SemiApplicative) /** * @category instances * @since 1.0.0 */ export const Applicative: applicative.Applicative = { imap, of, map, product, productMany, productAll } /** * Monoid that models the combination of computations that can fail, if at least one element is `None` * then the resulting combination is `None`, otherwise if all elements are `Some` then the resulting combination * is the combination of the wrapped elements using the provided `Monoid`. * * The `empty` value is `some(M.empty)`. * * See also `getFailureSemigroup` if you need a `Semigroup` instead of a `Monoid`. * * @category combining * @since 1.0.0 */ export const getFailureMonoid: (M: Monoid) => Monoid> = applicative.getMonoid( Applicative ) const coproduct = (self: Option, that: Option): Option => isSome(self) ? self : that const coproductMany = (self: Option, collection: Iterable>): Option => { let out = self if (isSome(out)) { return out } for (out of collection) { if (isSome(out)) { return out } } return out } /** * @category instances * @since 1.0.0 */ export const SemiCoproduct: semiCoproduct.SemiCoproduct = { imap, coproduct, coproductMany } /** * Semigroup returning the first `Some` value encountered. * * @category combining * @since 1.0.0 */ export const getFirstSomeSemigroup: () => Semigroup> = semiCoproduct .getSemigroup(SemiCoproduct) /** * @category instances * @since 1.0.0 */ export const Coproduct: coproduct_.Coproduct = { imap, coproduct, coproductMany, zero: none, coproductAll: firstSomeOf } /** * @category instances * @since 1.0.0 */ export const SemiAlternative: semiAlternative.SemiAlternative = { map, imap, coproduct, coproductMany } /** * @category instances * @since 1.0.0 */ export const Alternative: alternative.Alternative = { map, imap, coproduct, coproductMany, coproductAll: firstSomeOf, zero: none } // ------------------------------------------------------------------------------------- // folding // ------------------------------------------------------------------------------------- /** * Reduces an `Iterable` of `Option` to a single value of type `B`, elements that are `None` are ignored. * * @param self - The Iterable of `Option` to be reduced. * @param b - The initial value of the accumulator. * @param f - The reducing function that takes the current accumulator value and the unwrapped value of an `Option`. * * @example * import { some, none, reduceCompact } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * const iterable = [some(1), none(), some(2), none()] * assert.deepStrictEqual(pipe(iterable, reduceCompact(0, (b, a) => b + a)), 3) * * @category folding * @since 1.0.0 */ export const reduceCompact: { (b: B, f: (b: B, a: A) => B): (self: Iterable>) => B (self: Iterable>, b: B, f: (b: B, a: A) => B): B } = dual( 3, (self: Iterable>, b: B, f: (b: B, a: A) => B): B => { let out: B = b for (const oa of self) { if (isSome(oa)) { out = f(out, oa.value) } } return out } ) /** * @category folding * @since 1.0.0 */ export const Foldable: foldable.Foldable = { reduce: dual( 3, (self: Option, b: B, f: (b: B, a: A) => B): B => isNone(self) ? b : f(b, self.value) ) } /** * Transforms an `Option` into an `Array`. * If the input is `None`, an empty array is returned. * If the input is `Some`, the value is wrapped in an array. * * @param self - The `Option` to convert to an array. * * @example * import { some, none, toArray } from '@fp-ts/core/Option' * * assert.deepStrictEqual(toArray(some(1)), [1]) * assert.deepStrictEqual(toArray(none()), []) * * @category conversions * @since 1.0.0 */ export const toArray: (self: Option) => Array = foldable.toArray(Foldable) // ------------------------------------------------------------------------------------- // filtering // ------------------------------------------------------------------------------------- /** * @category filtering * @since 1.0.0 */ export const partitionMap: { (f: (a: A) => Either): (self: Option) => [Option, Option] (self: Option, f: (a: A) => Either): [Option, Option] } = dual(2, ( self: Option, f: (a: A) => Either ): [Option, Option] => { if (isNone(self)) { return [none(), none()] } const e = f(self.value) return either.isLeft(e) ? [some(e.left), none()] : [none(), some(e.right)] }) /** * Maps over the value of an `Option` and filters out `None`s. * * Useful when in addition to filtering you also want to change the type of the `Option`. * * @param self - The `Option` to map over. * @param f - A function to apply to the value of the `Option`. * * @category filtering * @since 1.0.0 */ export const filterMap: { (f: (a: A) => Option): (self: Option) => Option (self: Option, f: (a: A) => Option): Option } = dual( 2, (self: Option, f: (a: A) => Option): Option => isNone(self) ? none() : f(self.value) ) /** * @category instances * @since 1.0.0 */ export const Filterable: filterable.Filterable = { partitionMap, filterMap } /** * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. * * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. * * @param predicate - A predicate function to apply to the `Option` value. * @param fb - The `Option` to filter. * * @category filtering * @since 1.0.0 */ export const filter: { (self: Option, refinement: (a: A) => a is B): Option (self: Option, predicate: (a: A) => boolean): Option (refinement: (a: A) => a is B): (self: Option) => Option (predicate: (a: A) => boolean): (self: Option) => Option } = filterable.filter(Filterable) // ------------------------------------------------------------------------------------- // traversing // ------------------------------------------------------------------------------------- /** * @category traversing * @since 1.0.0 */ export const traverse = ( F: applicative.Applicative ): { ( f: (a: A) => Kind ): (self: Option) => Kind> (self: Option, f: (a: A) => Kind): Kind> } => dual( 2, ( self: Option, f: (a: A) => Kind ): Kind> => isNone(self) ? F.of(none()) : F.map(f(self.value), some) ) /** * @category instances * @since 1.0.0 */ export const Traversable: traversable.Traversable = { traverse } /** * @category traversing * @since 1.0.0 */ export const sequence: ( F: applicative.Applicative ) => (self: Option>) => Kind> = traversable .sequence(Traversable) /** * @category traversing * @since 1.0.0 */ export const traverseTap: ( F: applicative.Applicative ) => { ( self: Option, f: (a: A) => Kind ): Kind> ( f: (a: A) => Kind ): (self: Option) => Kind> } = traversable.traverseTap(Traversable) // ------------------------------------------------------------------------------------- // equivalence // ------------------------------------------------------------------------------------- /** * @example * import { none, some, getEquivalence } from '@fp-ts/core/Option' * import * as N from '@fp-ts/core/Number' * * const isEquivalent = getEquivalence(N.Equivalence) * assert.deepStrictEqual(isEquivalent(none(), none()), true) * assert.deepStrictEqual(isEquivalent(none(), some(1)), false) * assert.deepStrictEqual(isEquivalent(some(1), none()), false) * assert.deepStrictEqual(isEquivalent(some(1), some(2)), false) * assert.deepStrictEqual(isEquivalent(some(1), some(1)), true) * * @category equivalence * @since 1.0.0 */ export const getEquivalence = (E: Equivalence): Equivalence> => equivalence.make((x, y) => x === y || (isNone(x) ? isNone(y) : isNone(y) ? false : E(x.value, y.value)) ) // ------------------------------------------------------------------------------------- // sorting // ------------------------------------------------------------------------------------- /** * The `Order` instance allows `Option` values to be compared with * `compare`, whenever there is an `Order` instance for * the type the `Option` contains. * * `None` is considered to be less than any `Some` value. * * @example * import { none, some, getOrder } from '@fp-ts/core/Option' * import * as N from '@fp-ts/core/Number' * import { pipe } from '@fp-ts/core/Function' * * const O = getOrder(N.Order) * assert.deepStrictEqual(O.compare(none(), none()), 0) * assert.deepStrictEqual(O.compare(none(), some(1)), -1) * assert.deepStrictEqual(O.compare(some(1), none()), 1) * assert.deepStrictEqual(O.compare(some(1), some(2)), -1) * assert.deepStrictEqual(O.compare(some(1), some(1)), 0) * * @category sorting * @since 1.0.0 */ export const getOrder = (O: Order): Order> => order.make((self, that) => isSome(self) ? (isSome(that) ? O.compare(self.value, that.value) : 1) : -1 ) // ------------------------------------------------------------------------------------- // lifting // ------------------------------------------------------------------------------------- /** * Lifts a binary function into `Option`. * * @param f - The function to lift. * * @category lifting * @since 1.0.0 */ export const lift2: (f: (a: A, b: B) => C) => { (self: Option, that: Option): Option (that: Option): (self: Option) => Option } = semiApplicative.lift2(SemiApplicative) /** * Transforms a `Predicate` function into a `Some` of the input value if the predicate returns `true` or `None` * if the predicate returns `false`. * * @param predicate - A `Predicate` function that takes in a value of type `A` and returns a boolean. * * @example * import * as O from '@fp-ts/core/Option' * * const getOption = O.liftPredicate((n: number) => n >= 0) * * assert.deepStrictEqual(getOption(-1), O.none()) * assert.deepStrictEqual(getOption(1), O.some(1)) * * @category lifting * @since 1.0.0 */ export const liftPredicate: { (refinement: Refinement): (c: C) => Option (predicate: Predicate): (b: B) => Option } = (predicate: Predicate) => (b: B) => predicate(b) ? some(b) : none() /** * Lifts an `Either` function to an `Option` function. * * @param f - Any variadic function that returns an `Either`. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * const parse = (s: string) => * isNaN(+s) ? E.left(`Error: ${s} is not a number`) : E.right(+s) * * const parseNumber = O.liftEither(parse) * * assert.deepEqual(parseNumber('12'), O.some(12)) * assert.deepEqual(parseNumber('not a number'), O.none()) * * @category lifting * @since 1.0.0 */ export const liftEither = , E, B>( f: (...a: A) => Either ) => (...a: A): Option => fromEither(f(...a)) // ------------------------------------------------------------------------------------- // utils // ------------------------------------------------------------------------------------- /** * Returns a function that checks if an `Option` contains a given value using a provided `Equivalence` instance. * * @param equivalent - An `Equivalence` instance to compare values of the `Option`. * @param self - The `Option` to apply the comparison to. * @param a - The value to compare against the `Option`. * * @example * import { some, none, contains } from '@fp-ts/core/Option' * import { Equivalence } from '@fp-ts/core/Number' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(some(2), contains(Equivalence)(2)), true) * assert.deepStrictEqual(pipe(some(1), contains(Equivalence)(2)), false) * assert.deepStrictEqual(pipe(none(), contains(Equivalence)(2)), false) * * @since 1.0.0 */ export const contains = (isEquivalent: (self: A, that: A) => boolean): { (a: A): (self: Option) => boolean (self: Option, a: A): boolean } => dual(2, (self: Option, a: A): boolean => isNone(self) ? false : isEquivalent(self.value, a)) /** * Check if a value in an `Option` type meets a certain predicate. * * @param self - The `Option` to check. * @param predicate - The condition to check. * * @example * import { some, none, exists } from '@fp-ts/core/Option' * import { pipe } from '@fp-ts/core/Function' * * const isEven = (n: number) => n % 2 === 0 * * assert.deepStrictEqual(pipe(some(2), exists(isEven)), true) * assert.deepStrictEqual(pipe(some(1), exists(isEven)), false) * assert.deepStrictEqual(pipe(none(), exists(isEven)), false) * * @since 1.0.0 */ export const exists: { (predicate: Predicate): (self: Option) => boolean (self: Option, predicate: Predicate): boolean } = dual( 2, (self: Option, predicate: Predicate): boolean => isNone(self) ? false : predicate(self.value) ) // ------------------------------------------------------------------------------------- // algebraic operations // ------------------------------------------------------------------------------------- /** * @category algebraic operations * @since 1.0.0 */ export const sum: { (self: Option, that: Option): Option (that: Option): (self: Option) => Option } = lift2(N.sum) /** * @category algebraic operations * @since 1.0.0 */ export const multiply: { (self: Option, that: Option): Option (that: Option): (self: Option) => Option } = lift2(N.multiply) /** * @category algebraic operations * @since 1.0.0 */ export const subtract: { (self: Option, that: Option): Option (that: Option): (self: Option) => Option } = lift2(N.subtract) /** * @category algebraic operations * @since 1.0.0 */ export const divide: { (self: Option, that: Option): Option (that: Option): (self: Option) => Option } = lift2(N.divide) /** * Sum all numbers in an iterable of `Option` ignoring the `None` values. * * @param self - The iterable of `Option` to be summed. * * @example * import { sumCompact, some, none } from '@fp-ts/core/Option' * * const iterable = [some(2), none(), some(3), none()] * assert.deepStrictEqual(sumCompact(iterable), 5) * * @category algebraic operations * @since 1.0.0 */ export const sumCompact = (self: Iterable>): number => { let out = 0 for (const oa of self) { if (isSome(oa)) { out += oa.value } } return out } /** * Multiply all numbers in an iterable of `Option` ignoring the `None` values. * * @param self - The iterable of `Option` to be multiplied. * * @example * import { multiplyCompact, some, none } from '@fp-ts/core/Option' * * const iterable = [some(2), none(), some(3), none()] * assert.deepStrictEqual(multiplyCompact(iterable), 6) * * @category algebraic operations * @since 1.0.0 */ export const multiplyCompact = (self: Iterable>): number => { let out = 1 for (const oa of self) { if (isSome(oa)) { const a: number = oa.value if (a === 0) { return 0 } out *= a } } return out } // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- /** * @category do notation * @since 1.0.0 */ export const bindTo: { (name: N): (self: Option) => Option<{ [K in N]: A }> (self: Option, name: N): Option<{ [K in N]: A }> } = invariant.bindTo(Invariant) const let_: { ( name: Exclude, f: (a: A) => B ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> ( self: Option, name: Exclude, f: (a: A) => B ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> } = covariant.let(Covariant) export { /** * @category do notation * @since 1.0.0 */ let_ as let } /** * @category do notation * @since 1.0.0 */ export const bind: { ( name: Exclude, f: (a: A) => Option ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> ( self: Option, name: Exclude, f: (a: A) => Option ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> } = chainable.bind(Chainable) /** * @category do notation * @since 1.0.0 */ export const Do: Option<{}> = of_.Do(Of) /** * A variant of `bind` that sequentially ignores the scope. * * @category do notation * @since 1.0.0 */ export const andThenBind: { ( name: Exclude, that: Option ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> ( self: Option, name: Exclude, that: Option ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> } = semiProduct.andThenBind(SemiProduct)