import { Monad , AbstractMonad } from "./monad" ; import { Either , left , right , isLeft , isRight } from "./either" ; import { Freer , liftF } from "./freer" ; import { deepEqual } from "./utils" ; export type F0 < Z > = ( ) => Z ; export type F1 < A , Z > = ( a : A ) => Z ; export type F2 < A , B , Z > = ( a : A , b : B ) => Z ; export type F3 < A , B , C , Z > = ( a : A , b : B , c : C ) => Z ; export type F4 < A , B , C , D , Z > = ( a : A , b : B , c : C , d : D ) => Z ; export type F5 < A , B , C , D , E , Z > = ( a : A , b : B , c : C , d : D , e : E ) => Z ; export type IOValue < A > = Call | CallP | ThrowE | CatchE ; export class Call { type : "call" = "call" ; constructor ( public fn , public args ) { } } export class CallP { type : "callP" = "callP" ; constructor ( public fn , public args ) { } } export class ThrowE { type : "throwE" = "throwE" ; constructor ( public error ) { } } export class CatchE { type : "catchE" = "catchE" ; constructor ( public handler , public io ) { } } export type IO < A > = Freer < IOValue < any > , A > ; export const IO = Freer ; export function withEffects < A , Z > ( f ) ; export function withEffects < A , B , Z > ( f ) ; export function withEffects < A , B , C , Z > ( f ) ; export function withEffects < A , B , C , D , Z > ( f ) ; export function withEffects < A , B , C , D , E , Z > ( f ) ; export function withEffects < A > ( fn ) { return ( ... args ) => liftF ( new Call ( fn , args ) ) ; } export function withEffectsP < A , Z > ( f ) ; export function withEffectsP < A , B , Z > ( f ) ; export function withEffectsP < A , B , C , Z > ( f ) ; export function withEffectsP < A , B , C , D , Z > ( f ) ; export function withEffectsP < A , B , C , D , E , Z > ( f ) ; export function withEffectsP < A > ( fn ) { return ( ... args ) => liftF ( new CallP ( fn , args ) ) ; } export function call < Z > ( f ) ; export function call < A , Z > ( f , a ) ; export function call < A , B , Z > ( f , a , b ) ; export function call < A , B , C , Z > ( f , a , b , c ) ; export function call < A , B , C , D , Z > ( f , a , b , c , d ) ; export function call < A , B , C , D , E , Z > ( f , a , b , c , d , e ) ; export function call ( fn , ... args ) { return liftF ( new Call ( fn , args ) ) ; } export function callP < Z > ( f ) ; export function callP < A , Z > ( f , a ) ; export function callP < A , B , Z > ( f , a , b ) ; export function callP < A , B , C , Z > ( f , a , b , c ) ; export function callP < A , B , C , D , Z > ( f , a , b , c , d ) ; export function callP < A , B , C , D , E , Z > ( f , a , b , c , d , e ) ; export function callP ( fn , ... args ) { return liftF ( new CallP ( fn , args ) ) ; } export function throwE ( error ) { return liftF ( new ThrowE ( error ) ) ; } export function catchE ( errorHandler , io ) { return liftF ( new CatchE ( errorHandler , io ) ) ; } export function doRunIO < A > ( e ) { return e . match < Promise < A > > ( { pure : a => Promise . resolve ( a ) , bind : ( io , cont ) => { switch ( io . type ) { case "call" : return runIO ( cont ( io . fn ( ... io . args ) ) ) ; case "callP" : return io . fn ( ... io . args ) . then ( ( a ) => runIO ( cont ( a ) ) ) ; case "catchE" : return doRunIO ( io . io ) . then ( ( a ) => runIO ( cont ( a ) ) ) . catch ( ( err ) => doRunIO ( io . handler ( err ) ) ) ; case "throwE" : return Promise . reject ( io . error ) ; } } } ) ; } export function runIO < A > ( e ) { return doRunIO ( e ) ; } function doTestIO < A > ( e , arr , ending , idx ) { e . match ( { pure : a2 => { if ( ending !== a2 ) { throw new Error ( `Pure value invalid, expected ${ ending } but saw ${ a2 }` ) ; } } , bind : ( io , cont ) => { const [ { val : io2 } , a ] = arr [ idx ] ; if ( ! deepEqual ( io , io2 ) ) { throw new Error ( `Value invalid, expected ${ io2 } but saw ${ io }` ) ; } else { doTestIO ( cont ( a ) , arr , ending , idx + 1 ) ; } } } ) ; } export function testIO < A > ( e , arr , a ) { doTestIO ( e , arr , a , 0 ) ; }