/** [[include:doc/maybe.md]] */
/** (keep typedoc from getting confused by the imports) */
import { err, ok } from './result';
import { curry1, isVoid } from './utils';
/**
  Discriminant for the `Just` and `Nothing` variants.

  You can use the discriminant via the `variant` property of `Maybe` instances
  if you need to match explicitly on it.
 */
export var Variant;
(function (Variant) {
    Variant["Just"] = "Just";
    Variant["Nothing"] = "Nothing";
})(Variant || (Variant = {}));
/**
  A `Just` instance is the *present* variant instance of the
  [`Maybe`](../modules/_maybe_.html#maybe) type, representing the presence of a
  value which may be absent. For a full discussion, see [the module
  docs](../modules/_maybe_.html).

  @typeparam T The type wrapped in this `Just` variant of `Maybe`.
 */
export class Just {
    /**
      Create an instance of `Maybe.Just` with `new`.
  
      @note While you *may* create the `Just` type via normal JavaScript
      class construction, it is not recommended for the functional style for
      which the library is intended. Instead, use [`Maybe.of`] (for the general
      case) or [`Maybe.just`] for this specific case.
  
      [`Maybe.of`]: ../modules/_maybe_.html#of
      [`Maybe.just`]: ../modules/_maybe_.html#just
  
      ```ts
      // Avoid:
      const aString = new Maybe.Just('characters');
  
      // Prefer:
      const aString = Maybe.just('characters);
      ```
  
      @param value
      The value to wrap in a `Maybe.Just`.
  
      `null` and `undefined` are allowed by the type signature so that the
      constructor may `throw` on those rather than constructing a type like
      `Maybe<undefined>`.
  
      @throws      If you pass `null` or `undefined`.
     */
    constructor(value) {
        /** `Just` is always [`Variant.Just`](../enums/_maybe_.variant#just). */
        this.variant = Variant.Just;
        if (isVoid(value)) {
            throw new Error('Tried to construct `Just` with `null` or `undefined`');
        }
        this.value = value;
    }
    /**
      Unwrap the contained value. A convenience method for functional idioms.
  
      A common scenario where you might want to use this is in a pipeline of
      functions:
  
      ```ts
      import Maybe, { Just } from 'true-myth/maybe';
  
      function getLengths(maybeStrings: Array<Maybe<string>>): Array<number> {
        return maybeStrings
          .filter(Maybe.isJust)
          .map(Just.unwrap)
          .map(s => s.length);
      }
      ```
     */
    static unwrap(theJust) {
        return theJust.value;
    }
    /** Method variant for [`Maybe.isJust`](../modules/_maybe_.html#isjust) */
    isJust() {
        return true;
    }
    /** Method variant for [`Maybe.isNothing`](../modules/_maybe_.html#isnothing) */
    isNothing() {
        return false;
    }
    /** Method variant for [`Maybe.map`](../modules/_maybe_.html#map) */
    map(mapFn) {
        return map(mapFn, this);
    }
    /** Method variant for [`Maybe.mapOr`](../modules/_maybe_.html#mapor) */
    mapOr(orU, mapFn) {
        return mapOr(orU, mapFn, this);
    }
    /** Method variant for [`Maybe.mapOrElse`](../modules/_maybe_.html#maporelse) */
    mapOrElse(orElseFn, mapFn) {
        return mapOrElse(orElseFn, mapFn, this);
    }
    /** Method variant for [`Maybe.match`](../modules/_maybe_.html#match) */
    match(matcher) {
        return match(matcher, this);
    }
    /** Method variant for [`Maybe.or`](../modules/_maybe_.html#or) */
    or(mOr) {
        return or(mOr, this);
    }
    /** Method variant for [`Maybe.orElse`](../modules/_maybe_.html#orelse) */
    orElse(orElseFn) {
        return orElse(orElseFn, this);
    }
    /** Method variant for [`Maybe.and`](../modules/_maybe_.html#and) */
    and(mAnd) {
        return and(mAnd, this);
    }
    /** Method variant for [`Maybe.andThen`](../modules/_maybe_.html#andthen) */
    andThen(andThenFn) {
        return andThen(andThenFn, this);
    }
    /** Method variant for [`Maybe.chain`](../modules/_maybe_.html#chain) */
    chain(chainFn) {
        return this.andThen(chainFn);
    }
    /** Method variant for [`Maybe.flatMap`](../modules/_maybe_.html#flatmap) */
    flatMap(flatMapFn) {
        return this.andThen(flatMapFn);
    }
    /** Method variant for [`Maybe.unsafelyUnwrap`](../modules/_maybe_.html#unsafelyunwrap) */
    unsafelyUnwrap() {
        return this.value;
    }
    /** Method variant for [`Maybe.unwrapOr`](../modules/_maybe_.html#unwrapor) */
    unwrapOr(defaultValue) {
        return unwrapOr(defaultValue, this);
    }
    /** Method variant for [`Maybe.unwrapOrElse`](../modules/_maybe_.html#unwraporelse) */
    unwrapOrElse(elseFn) {
        return unwrapOrElse(elseFn, this);
    }
    /** Method variant for [`Maybe.toOkOrErr`](../modules/_maybe_.html#tookorerr) */
    toOkOrErr(error) {
        return toOkOrErr(error, this);
    }
    /** Method variant for [`Maybe.toOkOrElseErr`](../modules/_maybe_.html#tookorelseerr) */
    toOkOrElseErr(elseFn) {
        return toOkOrElseErr(elseFn, this);
    }
    /** Method variant for [`Maybe.toString`](../modules/_maybe_.html#tostring) */
    toString() {
        return toString(this);
    }
    /** Method variant for [`Maybe.equals`](../modules/_maybe_.html#equals) */
    equals(comparison) {
        return equals(comparison, this);
    }
    /** Method variant for [`Maybe.ap`](../modules/_maybe_.html#ap) */
    ap(val) {
        return ap(this, val);
    }
    /**
      Method variant for [`Maybe.get`](../modules/_maybe_.html#prop)
  
          If you have a `Maybe` of an object type, you can do `thatMaybe.get('a key')`
      to look up the next layer down in the object.
  
      ```ts
      type DeepOptionalType = {
        something?: {
          with?: {
            deeperKeys?: string;
          }
        }
      };
  
      const fullySet: DeepType = {
        something: {
          with: {
            deeperKeys: 'like this'
          }
        }
      };
  
      const deepJust = Maybe.of(fullySet)
        .get('something')
        .get('with')
        .get('deeperKeys');
  
      console.log(deepJust); // Just('like this');
  
      const partiallyUnset: DeepType = { something: { } };
  
      const deepEmpty = Maybe.of(partiallyUnset)
        .get('something')
        .get('with')
        .get('deeperKeys');
  
      console.log(deepEmpty); // Nothing
      ```
     */
    get(key) {
        return this.andThen(property(key));
    }
}
/**
  A `Nothing` instance is the *absent* variant instance of the
  [`Maybe`](../modules/_maybe_.html#maybe) type, representing the presence of a
  value which may be absent. For a full discussion, see [the module
  docs](../modules/_maybe_.html).

  @typeparam T The type which would be wrapped in a `Just` variant of `Maybe`.
 */
export class Nothing {
    /**
      Create an instance of `Maybe.Nothing` with `new`.
  
      @note While you *may* create the `Nothing` type via normal JavaScript
      class construction, it is not recommended for the functional style for
      which the library is intended. Instead, use [`Maybe.of`] (for the general
      case) or [`Maybe.nothing`] for this specific case.
  
      [`Maybe.of`]: ../modules/_maybe_.html#of
      [`Maybe.nothing`]: ../modules/_maybe_.html#nothing
  
      ```ts
      // Avoid:
      const aNothing = new Maybe.Err();
  
      // Prefer:
      const aNothing = Maybe.nothing();
      ```
  
      `null` and `undefined` are allowed so that you may explicitly construct the
      `Err` type with a known `null` or `undefined` value. (This maybe helpful
      primarily when transitioning a codebase to the use of `Maybe`.)
  
      @throws      If you pass `null` or `undefined`.
     */
    constructor(_) {
        /** `Nothing` is always [`Variant.Nothing`](../enums/_maybe_.variant#nothing). */
        this.variant = Variant.Nothing;
        /* nothing to do */
    }
    /** Method variant for [`Maybe.isJust`](../modules/_maybe_.html#isjust) */
    isJust() {
        return false;
    }
    /** Method variant for [`Maybe.isNothing`](../modules/_maybe_.html#isnothing) */
    isNothing() {
        return true;
    }
    /** Method variant for [`Maybe.map`](../modules/_maybe_.html#map) */
    map(mapFn) {
        return map(mapFn, this);
    }
    /** Method variant for [`Maybe.mapOr`](../modules/_maybe_.html#mapor) */
    mapOr(orU, mapFn) {
        return mapOr(orU, mapFn, this);
    }
    /** Method variant for [`Maybe.mapOrElse`](../modules/_maybe_.html#maporelse) */
    mapOrElse(orElseFn, mapFn) {
        return mapOrElse(orElseFn, mapFn, this);
    }
    /** Method variant for [`Maybe.match`](../modules/_maybe_.html#match) */
    match(matcher) {
        return match(matcher, this);
    }
    /** Method variant for [`Maybe.or`](../modules/_maybe_.html#or) */
    or(mOr) {
        return or(mOr, this);
    }
    /** Method variant for [`Maybe.orElse`](../modules/_maybe_.html#orelse) */
    orElse(orElseFn) {
        return orElse(orElseFn, this);
    }
    /** Method variant for [`Maybe.and`](../modules/_maybe_.html#and) */
    and(mAnd) {
        return and(mAnd, this);
    }
    /** Method variant for [`Maybe.andThen`](../modules/_maybe_.html#andthen) */
    andThen(andThenFn) {
        return andThen(andThenFn, this);
    }
    /** Method variant for [`Maybe.chain`](../modules/_maybe_.html#chain) */
    chain(chainFn) {
        return this.andThen(chainFn);
    }
    /** Method variant for [`Maybe.flatMap`](../modules/_maybe_.html#flatmap) */
    flatMap(flatMapFn) {
        return this.andThen(flatMapFn);
    }
    /** Method variant for [`Maybe.unsafelyUnwrap`](../modules/_maybe_.html#unsafelyunwrap) */
    unsafelyUnwrap() {
        throw new Error('Tried to `unsafelyUnwrap(Nothing)`');
    }
    /** Method variant for [`Maybe.unwrapOr`](../modules/_maybe_.html#unwrapor) */
    unwrapOr(defaultValue) {
        return unwrapOr(defaultValue, this);
    }
    /** Method variant for [`Maybe.unwrapOrElse`](../modules/_maybe_.html#unwraporelse) */
    unwrapOrElse(elseFn) {
        return unwrapOrElse(elseFn, this);
    }
    /** Method variant for [`Maybe.toOkOrErr`](../modules/_maybe_.html#tookorerr) */
    toOkOrErr(error) {
        return toOkOrErr(error, this);
    }
    /** Method variant for [`Maybe.toOkOrElseErr`](../modules/_maybe_.html#tookorelseerr) */
    toOkOrElseErr(elseFn) {
        return toOkOrElseErr(elseFn, this);
    }
    /** Method variant for [`Maybe.toString`](../modules/_maybe_.html#tostring) */
    toString() {
        return toString(this);
    }
    /** Method variant for [`Maybe.equals`](../modules/_maybe_.html#equals) */
    equals(comparison) {
        return equals(comparison, this);
    }
    /** Method variant for [`Maybe.ap`](../modules/_maybe_.html#ap) */
    ap(val) {
        return ap(this, val);
    }
    /**
      Method variant for [`Maybe.get`](../modules/_maybe_.html#prop)
  
          If you have a `Maybe` of an object type, you can do `thatMaybe.get('a key')`
      to look up the next layer down in the object.
  
      ```ts
      type DeepOptionalType = {
        something?: {
          with?: {
            deeperKeys?: string;
          }
        }
      };
  
      const fullySet: DeepType = {
        something: {
          with: {
            deeperKeys: 'like this'
          }
        }
      };
  
      const deepJust = Maybe.of(fullySet)
        .get('something')
        .get('with')
        .get('deeperKeys');
  
      console.log(deepJust); // Just('like this');
  
      const partiallyUnset: DeepType = { something: { } };
  
      const deepEmpty = Maybe.of(partiallyUnset)
        .get('something')
        .get('with')
        .get('deeperKeys');
  
      console.log(deepEmpty); // Nothing
      ```
     */
    get(key) {
        return this.andThen(property(key));
    }
}
/**
  Is this result a `Just` instance?

  @typeparam T The type of the wrapped value.
  @param maybe The `Maybe` instance to check.
  @returns     `true` if `maybe` is `Just`, `false` otherwise. In TypeScript,
               also narrows the type from `Maybe<T>` to `Just<T>`.
 */
export function isJust(maybe) {
    return maybe.variant === Variant.Just;
}
/**
  Is this result a `Nothing` instance?

  @typeparam T The type of the wrapped value.
  @param maybe The `Maybe` instance to check.
  @returns     `true` if `maybe` is `nothing`, `false` otherwise. In TypeScript,
               also narrows the type from `Maybe<T>` to `Nothing<T>`.
 */
export function isNothing(maybe) {
    return maybe.variant === Variant.Nothing;
}
/**
  Create an instance of `Maybe.Just`.

  `null` and `undefined` are allowed by the type signature so that the
  function may `throw` on those rather than constructing a type like
  `Maybe<undefined>`.

  @typeparam T The type of the item contained in the `Maybe`.
  @param value The value to wrap in a `Maybe.Just`.
  @returns     An instance of `Maybe.Just<T>`.
  @throws      If you pass `null` or `undefined`.
 */
export function just(value) {
    return new Just(value);
}
/**
  Create an instance of `Maybe.Nothing`.

  If you want to create an instance with a specific type, e.g. for use in a
  function which expects a `Maybe<T>` where the `<T>` is known but you have no
  value to give it, you can use a type parameter:

  ```ts
  const notString = Maybe.nothing<string>();
  ```

  @typeparam T The type of the item contained in the `Maybe`.
  @returns     An instance of `Maybe.Nothing<T>`.
 */
export function nothing(_) {
    return new Nothing(_);
}
/**
  Create a `Maybe` from any value.

  To specify that the result should be interpreted as a specific type, you may
  invoke `Maybe.of` with an explicit type parameter:

  ```ts
  const foo = Maybe.of<string>(null);
  ```

  This is usually only important in two cases:

  1.  If you are intentionally constructing a `Nothing` from a known `null` or
      undefined value *which is untyped*.
  2.  If you are specifying that the type is more general than the value passed
      (since TypeScript can define types as literals).

  @typeparam T The type of the item contained in the `Maybe`.
  @param value The value to wrap in a `Maybe`. If it is `undefined` or `null`,
               the result will be `Nothing`; otherwise it will be the type of
               the value passed.
 */
export function of(value) {
    return isVoid(value) ? nothing() : just(value);
}
/** Alias for [`of`](#of), primarily for compatibility with Folktale. */
export const fromNullable = of;
export function map(mapFn, maybe) {
    const op = (m) => (m.isJust() ? just(mapFn(m.value)) : nothing());
    return curry1(op, maybe);
}
export function mapOr(orU, mapFn, maybe) {
    function fullOp(fn, m) {
        return m.isJust() ? fn(m.value) : orU;
    }
    function partialOp(fn, curriedMaybe) {
        return curriedMaybe !== undefined
            ? fullOp(fn, curriedMaybe)
            : (extraCurriedMaybe) => fullOp(fn, extraCurriedMaybe);
    }
    return mapFn === undefined
        ? partialOp
        : maybe === undefined
            ? partialOp(mapFn)
            : partialOp(mapFn, maybe);
}
export function mapOrElse(orElseFn, mapFn, maybe) {
    function fullOp(fn, m) {
        return m.isJust() ? fn(m.value) : orElseFn();
    }
    function partialOp(fn, curriedMaybe) {
        return curriedMaybe !== undefined
            ? fullOp(fn, curriedMaybe)
            : (extraCurriedMaybe) => fullOp(fn, extraCurriedMaybe);
    }
    if (mapFn === undefined) {
        return partialOp;
    }
    else if (maybe === undefined) {
        return partialOp(mapFn);
    }
    else {
        return partialOp(mapFn, maybe);
    }
}
export function and(andMaybe, maybe) {
    const op = (m) => (m.isJust() ? andMaybe : nothing());
    return curry1(op, maybe);
}
export function andThen(thenFn, maybe) {
    const op = (m) => (m.isJust() ? thenFn(m.value) : nothing());
    return maybe !== undefined ? op(maybe) : op;
}
/** Alias for [`andThen`](#andthen). */
export const chain = andThen;
/** Alias for [`andThen`](#andthen). */
export const flatMap = andThen;
export function or(defaultMaybe, maybe) {
    const op = (m) => (m.isJust() ? m : defaultMaybe);
    return maybe !== undefined ? op(maybe) : op;
}
export function orElse(elseFn, maybe) {
    const op = (m) => (m.isJust() ? m : elseFn());
    return curry1(op, maybe);
}
/**
  Get the value out of the `Maybe`.

  Returns the content of a `Just`, but **throws if the `Maybe` is `Nothing`**.
  Prefer to use [`unwrapOr`](#unwrapor) or [`unwrapOrElse`](#unwraporelse).

  @typeparam T The type of the wrapped value.
  @param maybe The value to unwrap
  @returns     The unwrapped value if the `Maybe` instance is `Just`.
  @throws      If the `maybe` is `Nothing`.
 */
export function unsafelyUnwrap(maybe) {
    return maybe.unsafelyUnwrap();
}
/** Alias for [`unsafelyUnwrap`](#unsafelyunwrap) */
export const unsafelyGet = unsafelyUnwrap;
/** Alias for [`unsafelyUnwrap`](#unsafelyunwrap) */
export const unsafeGet = unsafelyUnwrap;
export function unwrapOr(defaultValue, maybe) {
    const op = (m) => (m.isJust() ? m.value : defaultValue);
    return curry1(op, maybe);
}
/** Alias for [`unwrapOr`](#unwrapor) */
export const getOr = unwrapOr;
export function unwrapOrElse(orElseFn, maybe) {
    const op = (m) => (m.isJust() ? m.value : orElseFn());
    return curry1(op, maybe);
}
/** Alias for [`unwrapOrElse`](#unwraporelse) */
export const getOrElse = unwrapOrElse;
export function toOkOrErr(error, maybe) {
    const op = (m) => (m.isJust() ? ok(m.value) : err(error));
    return maybe !== undefined ? op(maybe) : op;
}
export function toOkOrElseErr(elseFn, maybe) {
    const op = (m) => (m.isJust() ? ok(m.value) : err(elseFn()));
    return curry1(op, maybe);
}
/**
  Construct a `Maybe<T>` from a `Result<T, E>`.

  If the `Result` is an `Ok`, wrap its value in `Just`. If the `Result` is an
  `Err`, throw away the wrapped `E` and transform to a `Nothing`.

  @typeparam T  The type of the value wrapped in a `Result.Ok` and in the `Just`
                of the resulting `Maybe`.
  @param result The `Result` to construct a `Maybe` from.
  @returns      `Just` if `result` was `Ok` or `Nothing` if it was `Err`.
 */
export function fromResult(result) {
    return result.isOk() ? just(result.value) : nothing();
}
/**
  Create a `String` representation of a `Maybe` instance.

  A `Just` instance will be printed as `Just(<representation of the value>)`,
  where the representation of the value is simply the value's own `toString`
  representation. For example:

  | call                                   | output                  |
  |----------------------------------------|-------------------------|
  | `toString(Maybe.of(42))`               | `Just(42)`              |
  | `toString(Maybe.of([1, 2, 3]))`        | `Just(1,2,3)`           |
  | `toString(Maybe.of({ an: 'object' }))` | `Just([object Object])` |
  | `toString(Maybe.nothing())`            | `Nothing`               |

  @typeparam T The type of the wrapped value; its own `.toString` will be used
               to print the interior contents of the `Just` variant.
  @param maybe The value to convert to a string.
  @returns     The string representation of the `Maybe`.
 */
export function toString(maybe) {
    const body = maybe.isJust() ? `(${maybe.value.toString()})` : '';
    return `${maybe.variant}${body}`;
}
export function match(matcher, maybe) {
    return maybe !== undefined
        ? mapOrElse(matcher.Nothing, matcher.Just, maybe)
        : (curriedMaybe) => mapOrElse(matcher.Nothing, matcher.Just, curriedMaybe);
}
/** Alias for [`match`](#match) */
export const cata = match;
export function equals(mb, ma) {
    return ma !== undefined
        ? ma.match({
            Just: aVal => mb.isJust() && mb.unsafelyUnwrap() === aVal,
            Nothing: () => isNothing(mb),
        })
        : (maybeA) => maybeA.match({
            Nothing: () => isNothing(mb),
            Just: aVal => mb.isJust() && mb.unsafelyUnwrap() === aVal,
        });
}
export function ap(maybeFn, maybe) {
    const op = (m) => m.match({
        Just: val => maybeFn.map(fn => fn(val)),
        Nothing: () => Maybe.nothing(),
    });
    return curry1(op, maybe);
}
/**
  Determine whether an item is an instance of `Just` or `Nothing`.

  @param item The item to check.
 */
export function isInstance(item) {
    return item instanceof Just || item instanceof Nothing;
}
export function find(predicate, array) {
    const op = (a) => Maybe.of(a.find(predicate));
    return curry1(op, array);
}
/**
  Safely get the first item from a list, returning `Just` the first item if the
  array has at least one item in it, or `Nothing` if it is empty.

  ## Examples

  ```ts
  let empty = [];
  Maybe.head(empty); // => Nothing

  let full = [1, 2, 3];
  Maybe.head(full); // => Just(1)
  ```

  @param array The array to get the first item from.
 */
export function head(array) {
    return Maybe.of(array[0]);
}
/** A convenience alias for `Maybe.head`. */
export const first = head;
/**
  Safely get the last item from a list, returning `Just` the last item if the
  array has at least one item in it, or `Nothing` if it is empty.

  ## Examples

  ```ts
  let empty = [];
  Maybe.last(empty); // => Nothing

  let full = [1, 2, 3];
  Maybe.last(full); // => Just(3)
  ```

  @param array The array to get the first item from.
 */
export function last(array) {
    return Maybe.of(array[array.length - 1]);
}
/**
  Convert the arguments to a single `Maybe`. Useful for dealing with arrays of
  `Maybe`s, via the spread operator.

  ## Examples

  ```ts
  import Maybe from 'true-myth/maybe';

  let valid = [Maybe.just(2), Maybe.just('three')];
  Maybe.all(...valid); // => Just([2, 'three']);

  let invalid = [Maybe.just(2), Maybe.nothing<string>()];
  Maybe.all(...invalid); // => Nothing
  ```

  ## Note on Spread

  This requires the use of the spread operator because (at least as of
  TypeScript 3.0), the type inference falls down when attempting to build this
  same type with an array directly. Moreover, this spread-based approach handles
  heteregenous arrays; TS *also* fails to infer correctly for anything but
  homogeneous arrays when using that approach.

  @param maybes The `Maybe`s to resolve to a single `Maybe`.
 */
export function all(...maybes) {
    let result = Maybe.just([]);
    maybes.forEach(maybe => {
        result = result.andThen(accumulatedMaybes => maybe.map(m => {
            accumulatedMaybes.push(m);
            return accumulatedMaybes;
        }));
    });
    return result;
}
export function tuple(maybes) {
    // @ts-ignore -- this doesn't type-check, but it works correctly.
    return all(...maybes);
}
export function property(key, obj) {
    const op = (a) => Maybe.of(a[key]);
    return curry1(op, obj);
}
export function get(key, maybeObj) {
    return curry1(Maybe.andThen(property(key)), maybeObj);
}
export const Maybe = {
    Variant,
    Just,
    Nothing,
    all,
    isJust,
    isNothing,
    just,
    nothing,
    of,
    find,
    first,
    fromNullable,
    head,
    last,
    map,
    mapOr,
    mapOrElse,
    and,
    andThen,
    chain,
    flatMap,
    or,
    orElse,
    unsafelyUnwrap,
    unsafelyGet,
    unsafeGet,
    unwrapOr,
    getOr,
    unwrapOrElse,
    getOrElse,
    toOkOrErr,
    toOkOrElseErr,
    fromResult,
    toString,
    tuple,
    match,
    cata,
    equals,
    ap,
    isInstance,
    property,
    get,
};
export default Maybe;
//# sourceMappingURL=maybe.js.map