import { foldFree , liftF } from 'fp-ts/lib/Free' ; import { identity } from 'fp-ts/lib/function' ; import { Identity , identity as id } from 'fp-ts/lib/Identity' ; import { delay , Task , task } from 'fp-ts/lib/Task' ; import fs from 'fs' ; interface File { handle : fs . promises . FileHandle ; isOpen : boolean ; } const OpsFURI = 'Ops' ; type OpsFURI = typeof OpsFURI ; declare module 'fp-ts/lib/HKT' { interface URI2HKT < A > { Ops : OpsF < A > ; } } type OpsF < A > = OpenFile < A > | ReadLine < A > | Log < A > | CloseFile < A > ; class OpenFile < A > { readonly _tag : 'OpenFile' = 'OpenFile' ; readonly _A ! : A ; readonly _URI ! : OpsFURI ; constructor ( readonly name , readonly next ) { } } class ReadLine < A > { readonly _tag : 'ReadLine' = 'ReadLine' ; readonly _A ! : A ; readonly _URI ! : OpsFURI ; constructor ( readonly file , readonly next ) { } } class Log < A > { readonly _tag : 'Log' = 'Log' ; readonly _A ! : A ; readonly _URI ! : OpsFURI ; constructor ( readonly message , readonly next ) { } } class CloseFile < A > { readonly _tag : 'CloseFile' = 'CloseFile' ; readonly _A ! : A ; readonly _URI ! : OpsFURI ; constructor ( readonly file , readonly next ) { } } const open = ( name ) => liftF ( new OpenFile ( name , identity ) ) ; const readLine = ( file ) => liftF ( new ReadLine ( file , identity ) ) ; const log = ( message ) => liftF ( new Log ( message , ( ) => void 0 ) ) ; const closeFile = ( file ) => liftF ( new CloseFile ( file , identity ) ) ; const program = open ( './src/episode-8/file.csv' ) . chain ( ( file ) => log ( `File is open: ${ file . isOpen }` ) . chain ( ( ) => readLine ( file ) ) ) . chain ( ( [ line , file ] ) => log ( line ) . chain ( ( ) => readLine ( file ) ) ) . chain ( ( [ line , file ] ) => log ( line ) . chain ( ( ) => closeFile ( file ) ) ) . chain ( ( file ) => log ( `File is open: ${ file . isOpen }` ) ) ; const exaustive = ( x ) : never => x ; let position = 0 ; const lines = [ 'first line' , 'second line' ] ; const identityInterpreter = < A > ( fa ) : Identity < A > => { switch ( fa . _tag ) { case 'OpenFile' : return id . of ( fa . next ( { isOpen : true , handle : null as unknown as fs . promises . FileHandle } ) ) ; case 'ReadLine' : return id . of ( fa . next ( [ lines [ position ++ ] , fa . file ] ) ) ; case 'Log' : return id . of ( fa . next ( ) ) ; case 'CloseFile' : return id . of ( fa . next ( { isOpen : false , handle : null as unknown as fs . promises . FileHandle } ) ) ; } return exaustive ( fa ) ; } ; const taskInterpreter = < A > ( fa ) : Task < A > => { switch ( fa . _tag ) { case 'OpenFile' : return new Task ( ( ) => fs . promises . open ( fa . name , 'r' ) . then ( ( handle ) => fa . next ( { isOpen : true , handle } ) ) ) ; case 'ReadLine' : console . timeLog ( 'TASK' , 'Read line' ) ; return new Task ( ( ) => fa . file . handle . read ( Buffer . from ( new ArrayBuffer ( 24 ) , 0 , 24 ) , 0 , 24 ) . then ( ( { buffer } ) => buffer . toString ( ) ) ) . chain ( ( line ) => task . of ( fa . next ( [ line , fa . file ] ) ) ) ; case 'Log' : console . log ( '>>>>>' , fa . message ) ; return delay ( 500 , undefined ) . chain ( ( ) => { console . timeLog ( 'TASK' , 'Log' ) ; return task . of ( fa . next ( ) ) ; } ) ; case 'CloseFile' : return new Task ( ( ) => fa . file . handle . close ( ) ) . chain ( ( ) => { console . timeLog ( 'TASK' , 'Close file' ) ; return task . of ( fa . next ( { ... fa . file , isOpen : false } ) ) ; } ) ; } return exaustive ( fa ) ; } ; console . log ( 'Id interpreter' ) ; const resId = foldFree ( id ) ( identityInterpreter , program ) ; console . log ( resId . value ) ; console . log ( '\nTask interpreter' ) ; const resTask = foldFree ( task ) ( taskInterpreter , program ) ; console . time ( 'TASK' ) ; resTask . run ( ) . then ( ( ) => console . timeEnd ( 'TASK' ) ) ;