|
// SEE LICENSE |
|
|
|
import { Maybe, MaybeNull } from "./Maybe" |
|
|
|
// Scala got a Some things right. |
|
// HUR HUR I AM HILLLARIOUS |
|
|
|
// "Opt" instead of "Option" due to Option being an html entity already |
|
|
|
export type MaybeOpt<T> = Opt<T> | MaybeNull<T> |
|
|
|
/** |
|
* @see http://www.scala-lang.org/api/current/scala/Option.html |
|
*/ |
|
export interface Opt<A> { |
|
/** |
|
* @return true if the option is an instance of Some, false otherwise |
|
*/ |
|
isDefined: boolean |
|
/** |
|
* @return true if the option is None, false otherwise |
|
*/ |
|
isEmpty: boolean |
|
/** |
|
* @return the option's value. |
|
*/ |
|
get(): Maybe<A> |
|
/** |
|
* @return true if this option is nonempty and the predicate `p` returns true |
|
* when applied to this Option's value. |
|
*/ |
|
exists(p: (a: A) => boolean): boolean |
|
/** |
|
* @return a `Some` containing the result of applying `f` to this `Option`'s value |
|
* if this `Option` is nonempty. |
|
*/ |
|
map<B>(f: (a: A) => B): Opt<B> |
|
/** |
|
* @return the result of applying `f` to this `Option`'s value if this |
|
* `Option` is nonempty. By supporting `undefined` or `B`, we make caller's |
|
* lives a little easier--we'll wrap the result in an `Option` for you. |
|
*/ |
|
flatMap<B>(f: (a: A) => MaybeOpt<B>): Opt<B> |
|
/** |
|
* @return this `Option` if it is both nonempty |
|
* and applying the predicate `p` to this `Option`'s value returns true. |
|
*/ |
|
filter(p: (a: A) => boolean): Opt<A> |
|
/** |
|
* Apply the given procedure `f` to the `Option`'s value |
|
* if this `Option` is nonempty. |
|
* @return this (for fluent or chaining calls) |
|
*/ |
|
forEach(f: (a: A) => void): Opt<A> |
|
/** |
|
* @return this `Option`'s value if this `Option` is nonempty, |
|
* otherwise return the result of evaluating `f`. |
|
*/ |
|
getOrElse(f: () => A): A |
|
/** |
|
* @return this `Option`'s value if this `Option` is nonempty, |
|
* otherwise return the result of evaluating `f`. |
|
*/ |
|
orElse(f: () => MaybeOpt<A>): Opt<A> |
|
|
|
/** |
|
* @param f will only be invoked if both `this` and `b` are defined |
|
*/ |
|
zip1<B, T>(b: MaybeOpt<B>, f: (a: A, b: B) => MaybeOpt<T>): Opt<T> |
|
|
|
zip2<B, C, T>( |
|
b: MaybeOpt<B>, |
|
c: MaybeOpt<C>, |
|
f: (a: A, b: B, c: C) => MaybeOpt<T> |
|
): Opt<T> |
|
|
|
zip3<B, C, D, T>( |
|
b: MaybeOpt<B>, |
|
c: MaybeOpt<C>, |
|
d: MaybeOpt<D>, |
|
f: (a: A, b: B, c: C, d: D) => MaybeOpt<T> |
|
): Opt<T> |
|
} |
|
|
|
namespace NoneImpl { |
|
export const isDefined = false |
|
export const isEmpty = true |
|
export function get(): any { |
|
return |
|
} |
|
export const exists = () => false |
|
const noop = () => NoneImpl |
|
export const map = noop |
|
export const flatMap = noop |
|
export const filter = noop |
|
export const forEach = noop |
|
export function getOrElse<A>(f: () => A): A { |
|
return f() |
|
} |
|
export function orElse<A>(f: () => MaybeOpt<A>): Opt<A> { |
|
return Opt(f()) |
|
} |
|
export const zip1 = noop |
|
export const zip2 = noop |
|
export const zip3 = noop |
|
} |
|
|
|
export const None: Opt<any> = NoneImpl |
|
|
|
export class Some<A> implements Opt<A> { |
|
readonly isDefined = true |
|
readonly isEmpty = false |
|
|
|
constructor(private readonly a: A) {} |
|
|
|
get(): A { |
|
return this.a |
|
} |
|
|
|
exists(f: (a: A) => boolean): boolean { |
|
return f(this.a) |
|
} |
|
|
|
map<B>(f: (a: A) => B): Opt<B> { |
|
return new Some(f(this.a)) |
|
} |
|
|
|
flatMap<B>(f: (a: A) => Opt<B> | MaybeNull<B>): Opt<B> { |
|
const b = f(this.a) |
|
return isOpt(b) ? b : Opt(b) |
|
} |
|
|
|
filter(f: (a: A) => boolean): Opt<A> { |
|
return Opt(f(this.a) ? this.a : undefined) |
|
} |
|
|
|
forEach(f: (a: A) => void): this { |
|
f(this.a) |
|
return this |
|
} |
|
|
|
getOrElse(_orElse: () => A): A { |
|
return this.a |
|
} |
|
|
|
orElse(_orElse: () => Opt<A>): Opt<A> { |
|
return this |
|
} |
|
|
|
zip1<B, T>(b: MaybeOpt<B>, f: (a: A, b: B) => MaybeOpt<T>): Opt<T> { |
|
return Opt(b).flatMap(eb => f(this.a, eb)) |
|
} |
|
|
|
zip2<B, C, T>( |
|
b: Opt<B>, |
|
c: Opt<C>, |
|
f: (a: A, b: B, c: C) => MaybeOpt<T> |
|
): Opt<T> { |
|
return Opt(b).flatMap(eb => Opt(c).flatMap(ec => f(this.a, eb, ec))) |
|
} |
|
|
|
zip3<B, C, D, T>( |
|
b: MaybeOpt<B>, |
|
c: MaybeOpt<C>, |
|
d: MaybeOpt<D>, |
|
f: (a: A, b: B, c: C, d: D) => MaybeOpt<T> |
|
): Opt<T> { |
|
return Opt(b).flatMap(eb => |
|
Opt(c).flatMap(ec => Opt(d).flatMap(ed => f(this.a, eb, ec, ed))) |
|
) |
|
} |
|
} |
|
|
|
/** |
|
* @see http://www.scala-lang.org/api/current/scala/Option.html |
|
*/ |
|
export function Opt<A>(a: MaybeOpt<A>): Opt<A> { |
|
return isOpt(a) ? a : a != null ? new Some(a) : None |
|
} |
|
|
|
export function isOpt<A>(a: MaybeOpt<A>): a is Opt<A> { |
|
return a instanceof Some || a === None |
|
} |