// @flow import { HKT } from './HKT' import type { Monoid } from './Monoid' import type { Applicative } from './Applicative' import type { Semigroup } from './Semigroup' import type { Monad } from './Monad' import type { Foldable } from './Foldable' import type { Alt } from './Alt' import type { Plus } from './Plus' import type { Alternative } from './Alternative' import type { Extend } from './Extend' import type { Setoid } from './Setoid' import type { Traversable } from './Traversable' import type { MonadError } from './MonadError' import { id } from './Identity' import { constant } from './Fun' class IsMaybe {} export type MaybeV = ?A; export type Maybe = HKT; export function inj(a: MaybeV): Maybe { return ((a: any): Maybe) } export function prj(fa: Maybe): MaybeV { return ((fa: any): MaybeV) } export function isNothing(x: Maybe): boolean { return x == Nothing } export function isJust(x: Maybe): boolean { return x != Nothing } export function empty(): Maybe { return Nothing } export const pempty = empty export function concat(semigroup: Semigroup): (fx: Maybe, fy: Maybe) => Maybe { return function concat(fx, fy) { const x = prj(fx) const y = prj(fy) if (x == null) { return fy } if (y == null) { return fx } return of(semigroup.concat(x, y)) } } export function getSemigroup(semigroup: Semigroup): Semigroup> { return { concat: concat(semigroup) } } export function getMonoid(semigroup: Semigroup): Monoid> { return { empty, concat: concat(semigroup) } } export function map(f: (a: A) => B, fa: Maybe): Maybe { const a = prj(fa) return a == null ? Nothing : inj(f(a)) } export function ap(fab: Maybe<(a: A) => B>, fa: Maybe): Maybe { const ab = prj(fab) return ab == null ? Nothing : map(ab, fa) } export function of(a: A): Maybe { return inj(a) } export function chain(f: (a: A) => Maybe, fa: Maybe): Maybe { return maybe(Nothing, f, fa) } export const Nothing: Maybe = inj(null) export function reduce(f: (b: B, a: A) => B, b: B, fa: Maybe): B { const a = prj(fa) return a == null ? b : f(b, a) } export function alt(fx: Maybe, fy: Maybe): Maybe { return fx == Nothing ? fy : fx } export function extend(f: (ea: Maybe) => B, ea: Maybe): Maybe { return isNothing(ea) ? Nothing : inj(f(ea)) } export function equals(setoid: Setoid, fx: Maybe, fy: Maybe): boolean { const x = prj(fx) const y = prj(fy) if (x == null || y == null) { return true } if (x != null || y != null) { return setoid.equals(x, y) } return false } export function traverse(applicative: Applicative, f: (a: A) => HKT, ta: Maybe): HKT> { const a = prj(ta) if (a == null) { return applicative.of(Nothing) } return applicative.map(of, f(a)) } export function throwError(e: void): Maybe { // eslint-disable-line no-unused-vars return Nothing } export function catchError(ma: Maybe, handler: (e: void) => Maybe): Maybe { const a = prj(ma) return a == null ? handler() : ma } export function getSetoid(setoid: Setoid): Setoid> { return { equals(fx, fy) { return equals(setoid, fx, fy) } } } export function maybe(b: B, f: (a: A) => B, fa: Maybe): B { return reduce((_, a) => f(a), b, fa) } export function fromMaybe(a: A, fa: Maybe): A { return maybe(a, id, fa) } export function fromJust(fa: Maybe): A { const a = prj(fa) if (a == null) { throw new Error('fromJust returned a Nothing') } return a } // Maybe monoid returning the leftmost non-Nothing value. export const first: Monoid> = { empty, concat: alt } export const toNothing = constant(maybe.Nothing) /* Do notation (experimental) Example: import * as maybe from '../Maybe' const x: Maybe = maybe.Do.of(3) .map(n => n * 2) .chain(n => maybe.of(n - 1)) .value */ export class Do { static of(a: A): Do { return new Do(of(a)) } value: Maybe; constructor(value: Maybe) { this.value = value } map(f: (a: A) => B): Do { return new Do(map(f, this.value)) } chain(f: (a: A) => Maybe): Do { return new Do(chain(f, this.value)) } } if (false) { // eslint-disable-line ({ map, ap, of, chain, reduce, alt, pempty, extend, traverse, throwError, catchError }: Monad & Foldable & Alt & Plus & Alternative & Extend & Traversable & MonadError<*, IsMaybe>) }