// @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>)
}