6.031TS
6.031TS — Software Construction
TypeScript Pilot — Spring 2021

Code Examples for Reading 25

Download

You can also download a ZIP file containing this code.

Seems to be necessary to have a triple-backtick block at the start of this page,
otherwise all the pre/code tags below aren't syntax highlighted.
So create a hidden one.

Counter.ts


// Type of Counter's listener callback
export type NumberListener = (numberReached:bigint) => void;

/** A mutable incrementing counter that calls its listeners each time it 
  * reaches a new number. */
export class Counter {

    private _value: bigint = 0n;
    private listeners: Array<NumberListener> = [];

    // Abstraction function
    //    AF(_value, listeners) = a counter currently at `_value`
    //       that sends events to the `listeners` whenever it changes
    // Rep invariant
    //    true


    /** Make a counter initially set to zero. */
    public constructor() {
    }

    /** @return the value of this counter. */
    public get value():bigint {
        return this._value;
    }
    
    /** Increment this counter. */
    public increment():void {
        ++this._value;
        this.callListeners();
    }

    /** Modifies this counter by adding a listener.
      * @param listener called by this counter when it changes. */
    public addEventListener(listener: NumberListener):void {
        this.listeners.push(listener);
    }

    /** Modifies this counter by removing a listener.
      * @param listener will no longer be called by this counter. */
    public removeEventListener(listener: NumberListener):void {
        for (let i = 0; i < this.listeners.length; ++i) {
            if (this.listeners[i] === listener) {
                this.listeners.splice(i, 1);
                return;
            }
        }
    }

    // Call all listeners to notify them about a new number
    private callListeners():void {
        // iterate over a copy of listeners so that we are re-entrant
        // (i.e. a listener may call add/removeNumberListener,
        // which would mutate the set in the midst of iteration)
        for (const listener of [...this.listeners]) {
            listener(this._value);
        }
    }
}



/**
 * Example client code, trying out a Counter with a couple listeners.
 */
function main() {
    const counter = new Counter();

    // listen for multiples of 113
    counter.addEventListener((value) => {
        if (value % 113n === 0n) {
            console.log(value, "is a multiple of 113");
        }
    });

    // listen for 10000, then exit the process
    counter.addEventListener((value) => {
        if (value === 10000n) {
            process.exit(0);
        }
    });

    // crank the counter
    while (true) {
        counter.increment();
    }
}    

if (require.main === module) {
    main();
}