import { Applicative , ApplicativeDictionary } from "./applicative" ; import { foldr } from "./foldable" ; import { Traversable } from "./traversable" ; import { mixin , id , arrayFlatten , F0 , F1 , F2 , F3 , F4 , F5 } from "./utils" ; export interface Monad < A > extends Applicative < A > { multi : boolean ; chain < B > ( f ) : Monad < B > ; flatten < B > ( ) : Monad < B > ; } export interface MonadDictionary extends ApplicativeDictionary { multi : boolean ; } export abstract class AbstractMonad < A > implements Monad < A > { abstract multi : boolean ; abstract of < B > ( b ) ; chain < B > ( f ) { return this . map ( f ) . flatten ( ) ; } flatten < B > ( ) { return this . chain ( id ) ; } map < B > ( f ) { return this . chain ( ( a ) => this . of ( f ( a ) ) ) ; } mapTo < B > ( b ) { return this . chain ( _ => this . of ( b ) ) ; } ap < B > ( m ) { return m . chain ( f => this . chain ( a => this . of ( f ( a ) ) ) ) ; } lift < T1 , R > ( f , m ) ; lift < T1 , T2 , R > ( f , m1 , m2 ) ; lift < T1 , T2 , T3 , R > ( f , m1 , m2 , m3 ) ; lift ( f , ... ms ) { const { of } = ms [ 0 ] ; switch ( f . length ) { case 1 : return ms [ 0 ] . map ( f ) ; case 2 : return ms [ 0 ] . chain ( ( a ) => ms [ 1 ] . chain ( ( b ) => of ( f ( a , b ) ) ) ) ; case 3 : return ms [ 0 ] . chain ( ( a ) => ms [ 1 ] . chain ( ( b ) => ms [ 2 ] . chain ( ( c ) => of ( f ( a , b , c ) ) ) ) ) ; } } } export function monad ( constructor ) { const p = constructor . prototype ; if ( ! ( "of" in p ) ) { throw new TypeError ( "Can't derive monad. `of` method missing." ) ; } if ( ! ( "chain" in p ) && ! ( "flatten" in p && "map" in p ) ) { throw new TypeError ( "Can't derive monad. Either `chain` or `flatten` and `map` method must be defined." ) ; } if ( ! ( "multi" in p ) ) { p . multi = false ; } if ( ! ( "multi" in constructor ) ) { ( < any > constructor ) . multi = false ; } mixin ( constructor , [ AbstractMonad ] ) ; } export function flatten < A > ( m ) ; export function flatten ( m ) ; export function flatten < A > ( m ) { if ( Array . isArray ( m ) ) { return arrayFlatten ( m ) ; } else { return m . flatten ( ) ; } } function arrayChain < A , B > ( f , m ) { let result = [ ] ; for ( let i = 0 ; i < m . length ; ++ i ) { const added = f ( m [ i ] ) ; for ( let j = 0 ; j < added . length ; ++ j ) { result . push ( added [ j ] ) ; } } return result ; } export function chain < A , B > ( f , m ) ; export function chain < A , B > ( f , m ) ; export function chain < A , B > ( f , m ) { if ( Array . isArray ( m ) ) { return arrayChain < A , B > ( f , m ) ; } else { return m . chain ( f ) ; } } function reportErrorInGenerator ( value ) { throw new Error ( "An incorrect value was yielded inside a generator function: " + value . toString ( ) ) ; } function singleGo ( doing , m , check ) { function doRec ( v ) { const result = doing . next ( v ) ; if ( result . done === true ) { return m . of ( result . value ) ; } else if ( check ( result . value ) === true ) { return result . value . chain ( doRec ) ; } else { reportErrorInGenerator ( result . value ) ; } } return m . chain ( doRec ) ; } function multiGo < M extends Monad < any > > ( gen , m , check , args ) { const doRec = function ( v , stateSoFar ) : any { const doing = gen ( ... args ) ; for ( const it of stateSoFar ) { doing . next ( it ) ; } const result = doing . next ( v ) ; if ( result . done === true ) { return m . of ( result . value ) ; } else if ( check ( result . value ) === true ) { const newStateSoFar = stateSoFar . concat ( v ) ; return result . value . chain ( ( vv ) => doRec ( vv , newStateSoFar ) ) ; } else { reportErrorInGenerator ( result . value ) ; } } ; return m . chain ( vv => doRec ( vv , [ undefined ] ) ) ; } function hasChain ( value ) { return value . chain !== undefined ; } export function beginGo ( gen , monad ? , args = [ ] ) { const iterator = gen ( ... args ) ; const { done , value } = iterator . next ( ) ; if ( done === true ) { if ( monad !== undefined ) { return monad . of ( value ) ; } else { throw new Error ( "The generator function never yielded a monad and no monad was specified." ) ; } } const check = monad !== undefined && ( < any > monad ) . is ? ( < any > monad ) . is : hasChain ; if ( ! check ( value ) ) { reportErrorInGenerator ( value ) ; } if ( value . multi === true ) { return multiGo ( gen , value , check , args ) ; } else { return singleGo ( iterator , value , check ) ; } } export function go ( gen , monad ? ) { return beginGo ( gen , monad , [ ] ) ; } export type II < A = any > = IterableIterator < A > ; export function fgo < A > ( gen , monad ? ) ; export function fgo < A , B > ( gen , monad ? ) ; export function fgo < A , B , C > ( gen , monad ? ) ; export function fgo < A , B , C , D > ( gen , monad ? ) ; export function fgo < A , B , C , D , E > ( gen , monad ? ) ; export function fgo ( gen , monad ? ) ; export function fgo ( gen , monad ? ) { return ( ... args ) => beginGo ( gen , monad , args ) ; }