import { Lambda1 , Lambda1_deps , Lambda1_toFunction , Lambda2 , Lambda2_deps , Lambda2_toFunction , Lambda3 , Lambda3_deps , Lambda3_toFunction , Lambda4 , Lambda4_deps , Lambda4_toFunction , Lambda5 , Lambda5_deps , Lambda5_toFunction , Lambda6 , Lambda6_deps , Lambda6_toFunction , toSources , lambda1 } from "./Lambda" ; import { Source , Vertex } from "./Vertex" ; import { Transaction } from "./Transaction" ; import { Lazy } from "./Lazy" ; import { Listener } from "./Listener" ; import { Stream , StreamWithSend } from "./Stream" ; import { Operational } from "./Operational" ; import { Tuple2 } from "./Tuple2" ; class LazySample < A > { constructor ( cell ) { this . cell = cell ; } cell : Cell < A > ; hasValue : boolean = false ; value : A = null ; } class ApplyState < A , B > { constructor ( ) { } f : ( a : A ) => B = null ; f_present : boolean = false ; a : A = null ; a_present : boolean = false ; } export class Cell < A > { private str : Stream < A > ; protected value : A ; protected valueUpdate : A ; private cleanup : ( ) => void ; protected lazyInitValue : Lazy < A > ; private vertex : Vertex ; constructor ( initValue , str ? ) { this . value = initValue ; if ( ! str ) { this . str = new Stream < A > ( ) ; this . vertex = new Vertex ( "ConstCell" , 0 , [ ] ) ; } else Transaction . run ( ( ) => this . setStream ( str ) ) ; } protected setStream ( str ) { this . str = str ; const me = this , src = new Source ( str . getVertex__ ( ) , ( ) => { return str . listen_ ( me . vertex , ( a ) => { if ( me . valueUpdate == null ) { Transaction . currentTransaction . last ( ( ) => { me . value = me . valueUpdate ; me . lazyInitValue = null ; me . valueUpdate = null ; } ) ; } me . valueUpdate = a ; } , false ) ; } ) ; this . vertex = new Vertex ( "Cell" , 0 , [ src ] ) ; let tmpVertexNULL = new Vertex ( "Cell::setStream" , 1e12 , [ ] ) ; this . vertex . register ( tmpVertexNULL ) ; Transaction . currentTransaction . last ( ( ) => { this . vertex . deregister ( tmpVertexNULL ) ; } ) ; } getVertex__ ( ) { return this . vertex ; } getStream__ ( ) { return this . str ; } /**
     * Sample the cell's current value.
     * <p>
     * It should generally be avoided in favour of  listen so you don't
     * miss any updates, but in many circumstances it makes sense.
     * <p>
     * NOTE: In the Java and other versions of Sodium, using sample() inside map(), filter() and
     * merge() is encouraged. In the Javascript/Typescript version, not so much, for the
     * following reason: The memory management is different in the Javascript version, and this
     * requires us to track all dependencies. In order for the use of sample() inside
     * a closure to be correct, the cell that was sample()d inside the closure would have to be
     * declared explicitly using the helpers lambda1(), lambda2(), etc. Because this is
     * something that can be got wrong, we don't encourage this kind of use of sample() in
     * Javascript. Better and simpler to use snapshot().
     * <p>
     * NOTE: If you need to sample() a cell, you have to make sure it's "alive" in terms of
     * memory management or it will ignore updates. To make a cell work correctly
     * with sample(), you have to ensure that it's being used. One way to guarantee this is
     * to register a dummy listener on the cell. It will also work to have it referenced
     * by something that is ultimately being listened to.
      sample ( ) { return Transaction . run ( ( ) => { return this . sampleNoTrans__ ( ) ; } ) ; } sampleNoTrans__ ( ) { return this . value ; } /**
     * A variant of  sample that works with  CellLoop s when they haven't been looped yet.
     * It should be used in any code that's general enough that it could be passed a  CellLoop .
     *  see Stream sampleLazy ( ) { const me = this ; return Transaction . run ( ( ) => me . sampleLazyNoTrans__ ( ) ) ; } sampleLazyNoTrans__ ( ) { const me = this , s = new LazySample < A > ( me ) ; Transaction . currentTransaction . sample ( ( ) => { s . value = me . valueUpdate != null ? me . valueUpdate : me . sampleNoTrans__ ( ) ; s . hasValue = true ; s . cell = null ; } ) ; return new Lazy < A > ( ( ) => { if ( s . hasValue ) return s . value ; else return s . cell . sample ( ) ; } ) ; } param f map < B > ( f ) { const c = this ; return Transaction . run ( ( ) => Operational . updates ( c ) . map ( f ) . holdLazy ( c . sampleLazy ( ) . map ( Lambda1_toFunction ( f ) ) ) ) ; } param fn lift < B , C > ( b , fn0 ) { const fn = Lambda2_toFunction ( fn0 ) , cf = this . map ( ( aa ) => ( bb ) => fn ( aa , bb ) ) ; return Cell . apply ( cf , b , toSources ( Lambda2_deps ( fn0 ) ) ) ; } param fn lift3 < B , C , D > ( b , c , fn0 ) { const fn = Lambda3_toFunction ( fn0 ) , mf = ( aa ) => ( bb ) => ( cc ) => fn ( aa , bb , cc ) , cf = this . map ( mf ) ; return Cell . apply ( Cell . apply < B , ( c : C ) => D > ( cf , b ) , c , toSources ( Lambda3_deps ( fn0 ) ) ) ; } param fn lift4 < B , C , D , E > ( b , c , d , fn0 ) { const fn = Lambda4_toFunction ( fn0 ) , mf = ( aa ) => ( bb ) => ( cc ) => ( dd ) => fn ( aa , bb , cc , dd ) , cf = this . map ( mf ) ; return Cell . apply ( Cell . apply ( Cell . apply < B , ( c : C ) => ( d : D ) => E > ( cf , b ) , c ) , d , toSources ( Lambda4_deps ( fn0 ) ) ) ; } param fn lift5 < B , C , D , E , F > ( b , c , d , e , fn0 ) { const fn = Lambda5_toFunction ( fn0 ) , mf = ( aa ) => ( bb ) => ( cc ) => ( dd ) => ( ee ) => fn ( aa , bb , cc , dd , ee ) , cf = this . map ( mf ) ; return Cell . apply ( Cell . apply ( Cell . apply ( Cell . apply < B , ( c : C ) => ( d : D ) => ( e : E ) => F > ( cf , b ) , c ) , d ) , e , toSources ( Lambda5_deps ( fn0 ) ) ) ; } param fn lift6 < B , C , D , E , F , G > ( b , c , d , e , f , fn0 ) { const fn = Lambda6_toFunction ( fn0 ) , mf = ( aa ) => ( bb ) => ( cc ) => ( dd ) => ( ee ) => ( ff ) => fn ( aa , bb , cc , dd , ee , ff ) , cf = this . map ( mf ) ; return Cell . apply ( Cell . apply ( Cell . apply ( Cell . apply ( Cell . apply < B , ( c : C ) => ( d : D ) => ( e : E ) => ( f : F ) => G > ( cf , b ) , c ) , d ) , e ) , f , toSources ( Lambda6_deps ( fn0 ) ) ) ; } /**
     * High order depenency traking. If any newly created sodium objects within a value of a cell of a sodium object
     * happen to accumulate state, this method will keep the accumulation of state up to date.
     */ public tracking ( extractor ) { const out = new StreamWithSend < A > ( null ) ; let vertex = new Vertex ( "tracking" , 0 , [ new Source ( this . vertex , ( ) => { let cleanup2 = ( ) => { } ; let updateDeps = ( a ) => { let lastCleanups2 = cleanup2 ; let deps = extractor ( a ) . map ( dep => dep . getVertex__ ( ) ) ; for ( let i = 0 ; i < deps . length ; ++ i ) { let dep = deps [ i ] ; vertex . childrn . push ( dep ) ; dep . increment ( Vertex . NULL ) ; } cleanup2 = ( ) => { for ( let i = 0 ; i < deps . length ; ++ i ) { let dep = deps [ i ] ; for ( let j = 0 ; j < vertex . childrn . length ; ++ j ) { if ( vertex . childrn [ j ] === dep ) { vertex . childrn . splice ( j , 1 ) ; break ; } } dep . decrement ( Vertex . NULL ) ; } } ; lastCleanups2 ( ) ; } ; updateDeps ( this . sample ( ) ) ; var cleanup1 = Operational . updates ( this ) . listen_ ( vertex , ( a ) => { updateDeps ( a ) ; out . send_ ( a ) ; } , false ) ; return ( ) => { cleanup1 ( ) ; cleanup2 ( ) ; } ; } ) ] ) ; out . setVertex__ ( vertex ) ; return out . holdLazy ( this . sampleLazy ( ) ) ; } /**
     * Lift an array of cells into a cell of an array.
     */ public static liftArray < A > ( ca ) { return Cell . _liftArray ( ca , 0 , ca . length ) ; } private static _liftArray < A > ( ca , fromInc , toExc ) { if ( toExc - fromInc == 0 ) { return new Cell < A [ ] > ( [ ] ) ; } else if ( toExc - fromInc == 1 ) { return ca [ fromInc ] . map ( a => [ a ] ) ; } else { let pivot = Math . floor ( ( fromInc + toExc ) / 2 ) ; return Cell . _liftArray ( ca , fromInc , pivot ) . lift ( Cell . _liftArray ( ca , pivot , toExc ) , ( array1 , array2 ) => ( ) => array1 . concat ( array2 ) ) . map ( x => x ( ) ) ; } } /**
     * Apply a value inside a cell to a function inside a cell. This is the
     * primitive for all function lifting.
     */ static apply < A , B > ( cf , ca , sources ? ) { return Transaction . run ( ( ) => { let pumping = false ; const state = new ApplyState < A , B > ( ) , out = new StreamWithSend < B > ( ) , cf_updates = Operational . updates ( cf ) , ca_updates = Operational . updates ( ca ) , pump = ( ) => { if ( pumping ) { return ; } pumping = true ; Transaction . currentTransaction . prioritized ( out . getVertex__ ( ) , ( ) => { let f = state . f_present ? state . f : cf . sampleNoTrans__ ( ) ; let a = state . a_present ? state . a : ca . sampleNoTrans__ ( ) ; out . send_ ( f ( a ) ) ; pumping = false ; } ) ; } , src1 = new Source ( cf_updates . getVertex__ ( ) , ( ) => { return cf_updates . listen_ ( out . getVertex__ ( ) , ( f ) => { state . f = f ; state . f_present = true ; pump ( ) ; } , false ) ; } ) , src2 = new Source ( ca_updates . getVertex__ ( ) , ( ) => { return ca_updates . listen_ ( out . getVertex__ ( ) , ( a ) => { state . a = a ; state . a_present = true ; pump ( ) ; } , false ) ; } ) ; out . setVertex__ ( new Vertex ( "apply" , 0 , [ src1 , src2 ] . concat ( sources ? sources : [ ] ) ) ) ; return out . holdLazy ( new Lazy < B > ( ( ) => cf . sampleNoTrans__ ( ) ( ca . sampleNoTrans__ ( ) ) ) ) ; } ) ; } /**
     * Unwrap a cell inside another cell to give a time-varying cell implementation.
     */ static switchC < A > ( cca ) { return Transaction . run ( ( ) => { const za = cca . sampleLazy ( ) . map ( ( ba ) => ba . sample ( ) ) , out = new StreamWithSend < A > ( ) ; let outValue = null ; let pumping = false ; const pump = ( ) => { if ( pumping ) { return ; } pumping = true ; Transaction . currentTransaction . prioritized ( out . getVertex__ ( ) , ( ) => { out . send_ ( outValue ) ; outValue = null ; pumping = false ; } ) ; } ; let last_ca = null ; const cca_value = Operational . value ( cca ) , src = new Source ( cca_value . getVertex__ ( ) , ( ) => { let kill2 = last_ca === null ? null : Operational . value ( last_ca ) . listen_ ( out . getVertex__ ( ) , ( a ) => { outValue = a ; pump ( ) ; } , false ) ; const kill1 = cca_value . listen_ ( out . getVertex__ ( ) , ( ca ) => { last_ca = ca ; let nextKill2 = Operational . value ( ca ) . listen_ ( out . getVertex__ ( ) , ( a ) => { outValue = a ; pump ( ) ; } , false ) ; if ( kill2 !== null ) kill2 ( ) ; kill2 = nextKill2 ; } , false ) ; return ( ) => { kill1 ( ) ; kill2 ( ) ; } ; } ) ; out . setVertex__ ( new Vertex ( "switchC" , 0 , [ src ] ) ) ; return out . holdLazy ( za ) ; } ) ; } /**
     * Unwrap a stream inside a cell to give a time-varying stream implementation.
     */ static switchS < A > ( csa ) { return Transaction . run ( ( ) => { const out = new StreamWithSend < A > ( ) , h2 = ( a ) => { out . send_ ( a ) ; } , src = new Source ( csa . getVertex__ ( ) , ( ) => { let kill2 = csa . sampleNoTrans__ ( ) . listen_ ( out . getVertex__ ( ) , h2 , false ) ; const kill1 = csa . getStream__ ( ) . listen_ ( out . getVertex__ ( ) , ( sa ) => { let nextKill2 = sa . listen_ ( out . getVertex__ ( ) , h2 , true ) ; kill2 ( ) ; kill2 = nextKill2 ; } , false ) ; return ( ) => { kill1 ( ) ; kill2 ( ) ; } ; } ) ; out . setVertex__ ( new Vertex ( "switchS" , 0 , [ src ] ) ) ; return out ; } ) ; } /**
     * When transforming a value from a larger type to a smaller type, it is likely for duplicate changes to become
     * propergated. This function insures only distinct changes get propergated.
     */ calm ( eq ) { return Operational . updates ( this ) . collectLazy ( this . sampleLazy ( ) , ( newValue , oldValue ) => { let result ; if ( eq ( newValue , oldValue ) ) { result = null ; } else { result = newValue ; } return new Tuple2 ( result , newValue ) ; } ) . filterNotNull ( ) . holdLazy ( this . sampleLazy ( ) ) ; } /**
     * This function is the same as calm, except you do not need to pass an eq function. This function will use (===)
     * as its eq function. I.E. calling calmRefEq() is the same as calm((a,b) => a === b).
     */ calmRefEq ( ) { return this . calm ( ( a , b ) => a === b ) ; } /**
     * Listen for updates to the value of this cell. This is the observer pattern. The
     * returned  Listener has a  Listener method to cause the
     * listener to be removed. This is an OPERATIONAL mechanism is for interfacing between
     * the world of I/O and for FRP.
     *  param h The handler to execute when there's a new value.
     *   You should make no assumptions about what thread you are called on, and the
     *   handler should not block. You are not allowed to use  CellSink *   or  StreamSink in the handler.
     *   An exception will be thrown, because you are not meant to use this to create
     *   your own primitives.
      listen ( h ) { return Transaction . run ( ( ) => { return Operational . value ( this ) . listen ( h ) ; } ) ; } see https for more info
      static 'fantasy-land/of' < A > ( a ) { return new Cell < A > ( a ) ; } 'fantasy-land/map' < B > ( f ) { return this . map ( f ) ; } 'fantasy-land/ap' < B > ( cf ) { return Cell . apply ( cf , this ) ; } }