/** @module maybe.ts */ import { HKT, Type, URI2HKT, URIS } from 'fp-ts/lib/HKT' import { isNullable, isNullyOrNaN } from './check' import { adtn, Applicative, Apply, ArrayApplicative, ArrayApply, HKT0 } from './helper-types' /* tslint:disable variable-name object-literal-sort-keys max-classes-per-file */ declare global { interface Array { _URI: 'Array' _A: T } } declare module 'fp-ts/lib/HKT' { interface URI2HKT { Array: A[] } } declare module 'fp-ts/lib/HKT' { interface URI2HKT { 'babakness/maybe': Maybe, } } export type Maybe = Nothing | Just export const Maybe = { _URI: 'babakness/maybe', of: ( a: A ): Maybe => { return Just.of( a ) // return isNully(a) ? nothing : Just.of(a) }, /** * Place nullable values (undefined and null) into a nothing, otherwise into a Just */ from: ( a: A | undefined | null ): Maybe => { return isNullable( a ) ? nothing : Just.of( a ) }, } export class Nothing { static readonly _URI: 'babakness/maybe' static value: Maybe = new Nothing() static of( value ) { return nothing } readonly _A!: A readonly _URI!: 'babakness/maybe' private constructor( ) { } fold( valueWhenNothing: B, doWhenJust: ( a: A ) => B ): B { return valueWhenNothing } reduce( doWhenJust: ( a: A ) => B , valueWhenNothing: B ): B { return valueWhenNothing } joinOrValue( a: A ): A { return a } joinOrDo( fn: () => A ): A { return fn() } map( fn: ( a: A ) => B ): Maybe { return nothing } mapNullable( fn: ( a: A ) => B | undefined | null ): Maybe { return nothing } chain( fn: ( a: A ) => Maybe ): Maybe { return nothing } isJust(): this is Just { return false } isNothing(): this is Nothing { return true } /** * A shortcut for Maybe1.ap( Maybe2.map( toTuple ) ) * with better type intelligence */ apTuple( fb: Maybe ): Maybe<[B, A]> { return nothing } ap( fab: Maybe<( a: A ) => B> ): Maybe { return nothing } ap_( this: Maybe<( b: B ) => C>, fb: Maybe ): Maybe { return nothing } sequence( this: Maybe> & ( Maybe> | Maybe> ), F: HKT0 | {of: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) } ): Type> { return ( F as any ).of( nothing ) } sequenceNullable( this: Maybe> & ( Maybe> | Maybe> ), F: HKT0 | {of: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) } ): Type> { return ( F as any ).of( nothing ) } traverse( this: Maybe , F: Applicative | ArrayApplicative, fn: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) ): Type> { return ( F as any ).of( nothing ) } traverseNullable( this: Maybe , F: Applicative | ArrayApplicative, fn: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) ): Type> { return ( F as any ).of( nothing ) } } export const nothing = Nothing.value export class Just { static readonly _URI: 'babakness/maybe' static of( value: T ) { return new Just( value ) } readonly _A!: A readonly _URI!: 'babakness/maybe' constructor( readonly value: A ) { } traverse( this: Maybe , F: Applicative | ArrayApplicative, fn: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) ): Type> { const functor = this.isJust() ? fn( this.value ) : nothing return ( Maybe as any ).of( functor ).sequence( F as any ) } traverseNullable( this: Maybe , F: Applicative | ArrayApplicative, fn: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) ): Type> { const functor = this.isJust() ? fn( this.value ) : nothing return ( Maybe as any ).from( functor ).sequenceNullable( F as any ) } // sequence( this: Maybe< HKT & Functor & Apply> , F: (Applicative> | ArrayApplicative>) ): Type> { sequence( this: Maybe> & ( Maybe> | Maybe> ), F: HKT0 | {of: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) } ): Type> { const functor = this.isJust() ? this.value : nothing const isArray = ( f ): f is HKT & ArrayApply => Array.isArray( f ) return ( isArray( functor ) ? functor[ adtn.ap ] : ( functor as any ).ap ) .call( functor, ( F as any ).of( Maybe.of ) ) // const x = map( ) } sequenceNullable( this: Maybe> & ( Maybe> | Maybe> ), F: HKT0 | {of: ( a: A ) => ( HKT & Apply ) | ( HKT & ArrayApply ) } ): Type> { const functor = this.isJust() ? this.value : nothing const isArray = ( f ): f is HKT & ArrayApply => Array.isArray( f ) return ( isArray( functor ) ? functor[ adtn.ap ] : ( functor as any ).ap ) .call( functor, ( F as any ).of( Maybe.from ) ) // const x = map( ) } joinOrValue( a: A ): A { return this.value } joinOrDo( fn: () => A ): A { return this.value } map( fn: ( a: A ) => B ): Maybe { // const gold = Maybe.flatten( Maybe.of( fn( this.value ) )) return Just.of( fn( this.value ) ) } mapNullable( fn: ( a: A ) => B | undefined | null ): Maybe { return Maybe.from( fn( this.value ) ) } chain( fn: ( a: A ) => Maybe ): Maybe { return fn( this.value ) } /** * A shortcut for Maybe1.ap( Maybe2.map( toTuple ) ) * with better type intelligence */ apTuple( fb: Maybe ): Maybe<[B, A]> { return fb.isJust() ? Just.of( [ fb.value, this.value ] ) as Just<[B, A]> : nothing } ap( fab: Maybe<( a: A ) => B> ): Maybe { return fab.isJust() ? Just.of( fab.value( this.value ) ) : nothing } ap_( this: Maybe<( b: B ) => C>, fb: Maybe ): Maybe { return fb.ap( this ) } fold( valueWhenNothing: B, doWhenJust: ( a: A ) => B ): B { return doWhenJust( this.value ) } reduce( doWhenJust: ( a: A ) => B, valueWhenNothing: B ): B { return doWhenJust( this.value ) } isJust(): this is Just { return true } isNothing(): this is Nothing { return false } }