/** @module lens.ts */ import { HKT, Type, URI2HKT3, URIS} from 'fp-ts/lib/HKT' import { head } from './array/head' import { init } from './array/init' import { last } from './array/last' import { isFunction } from './check' import { defineFunctionProperties } from './function/defineFunctionProperties' import { pipeline } from './function/pipeline' import { untypedCurry } from './function/untypedCurry' import { Omit } from './helper-types' import { DELETE, lens } from './lens-functions' // import { trace } from './logging' import { getPathValue } from './object' type Path = Array type ProgramHead = [ Path, A ] type ChainingFn = ( data: Y ) => ( pathValue: A ) => Lens type MappingFn = ( data: Y ) => ( pathValue: A ) => B type MapOrChainFn = ChainingFn | MappingFn interface Programs extends Array { [index: number]: ( ProgramHead | ChainingFn | MappingFn ) 0: ProgramHead } type PathValue = NonNullable // | typeof UNCHANGED | typeof DELETE export class Lens { static readonly _URI: 'babakness/lens' static type() { return new Lens( [] as any ) } static typeChange() { return new Lens( [] as any ) } // static of( pathAndDefaultValue: [ [K1] , typeof DELETE]): Lens,PathValue | undefined> static of( pathAndDefaultValue: [[K1] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [ [K1, K2] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [[K1, K2, K3] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [ [K1, K2, K3, K4] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [ [K1, K2, K3, K4, K5] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [ [K1, K2, K3, K4, K5, K6] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [ [K1, K2, K3, K4, K5, K6, K7] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [[K1, K2, K3, K4, K5, K6, K7, K8] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [[K1, K2, K3, K4, K5, K6, K7, K8, K9] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue: [[K1, K2, K3, K4, K5, K6, K7, K8, K9, K10] , PathValue] ): Lens, PathValue, PathValue> static of( pathAndDefaultValue ) { return new Lens( [ pathAndDefaultValue ] ) } private static readonly chainHelperName = 'chainHelper' private static readonly isComposableLensMorphism = ( item ): item is ( data: E ) => ( ( pathValue: C ) => D ) => typeof item === 'function' && item.name !== Lens.chainHelperName readonly _A!: A readonly _L!: V readonly _U!: Y readonly _URI!: 'babakness/lens' constructor( readonly program: Programs ) { } // Initial Object, Final Object, Initial Type, Current Type // of( this: Lens, pathAndDefaultValue: [ [K1] , typeof DELETE]): Lens,PathValue,PathValue,PathValue> // of( pathAndDefaultValue: [ [K1] , typeof DELETE]): Lens,PathValue | undefined> of( pathAndDefaultValue: [[K1] , PathValue] ): Lens, PathValue, PathValue> // of( pathAndDefaultValue: [ [K1] , typeof DELETE]): Lens,typeof DELETE,PathValue> // of

,K1 extends keyof Y = keyof Y >( pathAndDefaultValue: [ [K1] , typeof DELETE]): Lens,typeof DELETE,PathValue> of( pathAndDefaultValue: [ [K1] , typeof DELETE] ): Lens : V, PathValue, typeof DELETE, PathValue> // of >( pathAndDefaultValue: [ [K1] , typeof DELETE]): Lens,typeof DELETE,PathValue> of( pathAndDefaultValue: [ [K1, K2] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [[K1, K2, K3] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [ [K1, K2, K3, K4] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [ [K1, K2, K3, K4, K5] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [ [K1, K2, K3, K4, K5, K6] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [ [K1, K2, K3, K4, K5, K6, K7] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [[K1, K2, K3, K4, K5, K6, K7, K8] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [[K1, K2, K3, K4, K5, K6, K7, K8, K9] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue: [[K1, K2, K3, K4, K5, K6, K7, K8, K9, K10] , PathValue] ): Lens, PathValue, PathValue> of( pathAndDefaultValue ): any { return new Lens( [ pathAndDefaultValue ] ) } map( fn: ( pathValue: A ) => B ): Lens { return new Lens( this.mapProgram( ( dataOut: Y ) => fn ) as Programs ) } ap( fab: Lens B> ): Lens { return new Lens( this.mapProgram( ( data ) => ( pathValue ) => fab.read( data )( pathValue ) ) as Programs, ) } chain( fn: ( pathValue: A ) => Lens ): Lens { return new Lens( this.chainProgram( ( data ) => fn ) as Programs ) } join

( this: Lens, dataIn: Y ): V join

( this: Lens, dataIn: Y ): V // join( dataIn: Y ): V join( this: Lens, dataIn ) { type Accumelator<_L, _A> = [ _L, Path, _A ] const handleChain = ( dataOut: Y , chainHelper: ChainingFn , path: Path, defaultValue: A ): Accumelator => { return pipeline( chainHelper( dataOut )( getPathValue( path as [keyof Y], dataOut ) as A ), ( _lens ) => [ ( _lens as any ).join( dataOut ), ...head( _lens.program ) as ProgramHead ] as Accumelator, ) } const applyMorphism = ( dataOut: Y, fn: MappingFn, path: Path, defaultValue: A ): Accumelator => [ lens( path as [keyof Y], fn( dataOut ) as ( pathValue: unknown ) => B, defaultValue, dataOut ) as any as V, path, defaultValue, ] const programReducer = ( [ dataOut , path , defaultValue ]: Accumelator, item: MapOrChainFn | ProgramHead ): Accumelator => { return isFunction( item ) // item is a function ? this.isChainHelper( item ) ? handleChain( dataOut, item, path, defaultValue ) as Accumelator : applyMorphism( dataOut, item as MappingFn, path, defaultValue ) as Accumelator // item is an array : [ dataOut, head( item as ProgramHead ), last( item as ProgramHead ) ] as Accumelator } const programHead = head( this.program ) return head( this.program.reduce( programReducer, [ dataIn, head( programHead ), last( programHead ), ], ) as Accumelator, ) } fold( data: Data, fn: ( pathValue: A ) => B ): Y fold( data: Data ): ( fn: ( pathValue: A ) => B ) => Y fold( ...args ) { return untypedCurry( ( fn, data ) => this.map( fn ).join( data ), )( ...args ) } write( writeValue: A ): ( data: Data ) => Y write( writeValue: A , data: Data ): Y write( ...args ) { return untypedCurry( ( writeValue, data ) => this.fold( data, ( _ ) => writeValue ), )( ...args ) } reduce( fn: ( pathValue: A ) => B ): ( data: Data ) => Y reduce( fn: ( pathValue: A ) => B , data: Data ): Y reduce( ...args ) { return untypedCurry( ( fn, data ) => this.map( fn ).join( data ), )( ...args ) } joinAndRead( data: Data ): [V, A, unknown] { const [ path, defaultValue ] = head( this.program ) const fn = ( a ) => a return pipeline( this.map( fn ).join( data ), ( pData ) => [ pData, getPathValue( path as [keyof V] , pData ), getPathValue( path as [keyof Y], data ) ], ) as [V, A, unknown] } reduceAndRead( fn: ( pathValue: A ) => B ): ( data: Data ) => V reduceAndRead( fn: ( pathValue: A ) => B , data: Data ): V reduceAndRead( ...args ) { return untypedCurry( ( fn, data ) => this.map( fn ).joinAndRead( data ), ) } read( data: Data ): A { const [ path, defaultValue ] = head( this.program ) return getPathValue( path as [keyof Y], this.reduce( ( a ) => a , data ) ) as A } preRead( data: Data ) { const [ path, defaultValue ] = head( this.program ) const foo = head( this.program ) return getPathValue( path as [keyof Y], data ) } private mapProgram( fn: MappingFn ): Programs { return pipeline( last( this.program ) as MapOrChainFn | ProgramHead, ( lastProgram ) => init( this.program ).concat( Lens.isComposableLensMorphism( lastProgram ) ? [ ( dataOut: Y ) => ( pathValue: A ) => fn( dataOut )( lastProgram( dataOut )( pathValue ) ) ] : [ lastProgram, fn ], ) as Programs, ) } private readonly isChainHelper = ( item ): item is ( ( data: Y ) => ( pathValue: A ) => Lens ) => typeof item === 'function' && item.name === Lens.chainHelperName private getChainHelper( fn: ( data: Y ) => ( pathValue: A ) => Lens ): ChainingFn { const that = this return defineFunctionProperties( function chainHelper( data ) { return ( pathValue: A ) => fn( data )( pathValue ) }, { name: Lens.chainHelperName }, ) } private chainProgram( fn: ( data: Y ) => ( pathValue: A ) => Lens ): Programs { return this.program.concat( this.getChainHelper( fn ) ) as Programs } } // declare module 'fp-ts/lib/HKT' { // interface URI2HKT3 { // 'babakness/lens': Lens, // } // }