import { Functor , AbstractFunctor , mapTo } from "./functor" ; import { Either } from "./either" ; import { id , mixin , apply , curry2 , curry3 } from "./utils" ; export interface ApplicativeDictionary { of : < B > ( b : B ) => Applicative < B > ; } export interface Applicative < A > extends Functor < A > { of : < B > ( b : B ) => Applicative < B > ; ap < B > ( a ) : Applicative < B > ; lift < T1 , R > ( f , m ) : Applicative < R > ; lift < T1 , T2 , R > ( f , m1 , m2 ) : Applicative < R > ; lift < T1 , T2 , T3 , R > ( f , m1 , m2 , m3 ) : Applicative < R > ; map < B > ( f ) : Applicative < B > ; } export abstract class AbstractApplicative < A > extends AbstractFunctor < A > implements Applicative < A > { abstract of < B > ( b ) ; ap < B > ( f ) { return this . lift < ( a : A ) => B , A , B > ( apply , f , this ) ; } lift < T1 , R > ( f , m ) ; lift < T1 , T2 , R > ( f , m1 , m2 ) ; lift < T1 , T2 , T3 , R > ( f , m1 , m2 , m3 ) ; lift ( ) { const f = arguments [ 0 ] ; switch ( arguments . length - 1 ) { case 1 : return arguments [ 1 ] . map ( f ) ; case 2 : return arguments [ 2 ] . ap ( arguments [ 1 ] . map ( curry2 ( f ) ) ) ; case 3 : return arguments [ 3 ] . ap ( arguments [ 2 ] . ap ( arguments [ 1 ] . map ( curry3 ( f ) ) ) ) ; } } map < B > ( f ) { return this . ap ( this . of ( f ) ) ; } } export function applicative ( constructor ) { const prototype = constructor . prototype ; if ( ! ( "of" in prototype ) ) { throw new TypeError ( "Can't derive applicative. `of` method missing." ) ; } if ( ! ( "ap" in prototype ) && ! ( "lift" in prototype ) ) { throw new TypeError ( "Can't derive applicative. Either `lift` or `ap` method must be defined." ) ; } mixin ( constructor , [ AbstractFunctor , AbstractApplicative ] ) ; } function isArrayConstructor ( a ) { return a === Array ; } export function of < A > ( d , a ) { if ( isArrayConstructor ( d ) ) { return [ a ] ; } else { return d . of ( a ) ; } } function arrayLift ( f , args , indices ) { if ( args . length === indices . length ) { let values = [ ] ; for ( let i = 0 ; i < args . length ; ++ i ) { values [ i ] = args [ i ] [ indices [ i ] ] ; } return [ f . apply ( undefined , values ) ] ; } else { let results = [ ] ; for ( let i = 0 ; i < args [ indices . length ] . length ; ++ i ) { results = results . concat ( arrayLift ( f , args , indices . concat ( i ) ) ) ; } return results ; } } export function ap < A , B > ( fa , ba ) { return ba . ap ( fa ) ; } export function lift < T1 , R > ( f , m ) ; export function lift < T1 , T2 , R > ( f , m1 , m2 ) ; export function lift < T1 , T2 , T3 , R > ( f , m1 , m2 , m3 ) ; export function lift < A , T1 , R > ( f , m ) ; export function lift < A , T1 , T2 , R > ( f , m1 , m2 ) ; export function lift < A , T1 , T2 , T3 , R > ( f , m1 , m2 , m3 ) ; export function lift < A , R > ( f , a ) ; export function lift < A , B , R > ( f , a , b ) ; export function lift < A , B , C , R > ( f , a , b , c ) ; export function lift ( f , ... args ) { if ( Array . isArray ( args [ 0 ] ) ) { return arrayLift ( f , args , [ ] ) ; } else { return args [ 0 ] . lift ( f , ... args ) ; } } export function seq < A , F extends Applicative < A > > ( a , b ) { return ap ( mapTo ( id , a ) , b ) ; }