Chapter 1: The Foundation of Everything: combineLatestWith
combineLatestWith is what embodies the concept of the Applicative Functor established in Unit 4—the operation of “combining independent containers with a binary operation”—in the Timeline library. It is a concrete implementation of the Applicative operation, designed to combine multiple independent, asynchronous sources.
The high-level composition functions that will appear in subsequent chapters, such as anyOf and sumOf, are all built on top of this primitive operation, combineLatestWith.
Origin of the Name
Section titled “Origin of the Name”The name combineLatestWith is a standard term used in other major reactive programming libraries (like RxJS) and accurately expresses its behavior. That is, it implies that it combines the latest values from multiple sources and generates a new value with the provided function.
API Definition
Section titled “API Definition”F#: combineLatestWith: ('a -> 'b -> 'c) -> Timeline<'a> -> Timeline<'b> -> Timeline<'c>
Section titled “F#: combineLatestWith: ('a -> 'b -> 'c) -> Timeline<'a> -> Timeline<'b> -> Timeline<'c>”TS: combineLatestWith<A, B, C>(f: (valA: A, valB: B) => C) => (timelineA: Timeline<A>) => (timelineB: Timeline<B>): Timeline<C>
Section titled “TS: combineLatestWith<A, B, C>(f: (valA: A, valB: B) => C) => (timelineA: Timeline<A>) => (timelineB: Timeline<B>): Timeline<C>”The type signature of combineLatestWith conforms to the map2 pattern of the Applicative Functor we learned in Unit 4.
This signature is composed of the following elements:
('a -> 'b -> 'c): A binary operation function. It takes a value of type'aas the first argument and a value of type'bas the second argument, and returns a result of type'c.Timeline<'a>: The first sourceTimeline. The latest value of thisTimelineis passed as the first argument to the binary operation function.Timeline<'b>: The second sourceTimeline. The latest value of thisTimelineis passed as the second argument to the binary operation function.Timeline<'c>: The newTimelinethat is generated as a result. The result of the binary operation function becomes the value of thisTimeline.
Behavior
Section titled “Behavior”The behavior of combineLatestWith is defined in two phases: initialization and update.
-
Initialization: When the result
Timelineis created,combineLatestWithimmediately checks the current values of both sourceTimelines and applies the binary operation function to determine the initial value of the resultTimeline. -
Update: The result
Timelinemonitors both sourceTimelines. Whenever either source is updated, it gets the latest current values from both sources, applies the binary operation function again, and defines a new value on the resultTimeline.
Practical Code Example
Section titled “Practical Code Example”Let’s create a Timeline that reactively calculates the sum of two independent counters.
// TSconst counterA = Timeline(10);const counterB = Timeline(20);
// Combine the two Timelines using a binary operation (+)const sumTimeline = combineLatestWith((a: number, b: number) => a + b)(counterA)(counterB);
// The initial value is 10 + 20 = 30console.log(sumTimeline.at(Now)); // 30
// Update counterAconsole.log("Updating CounterA to 15...");counterA.define(Now, 15);
// sumTimeline is automatically recalculated (15 + 20 = 35)console.log("Current Sum after A updated: %d", sumTimeline.at(Now)); // 35Internal Implementation
Section titled “Internal Implementation”Internally, this operation registers dependencies from the two source Timelines to the result Timeline in DependencyCore. Also, by using the FinalizationRegistry in timeline.ts (equivalent to GcRegistry in F#), it has a memory-safe design where these dependencies are automatically cleaned up when the result Timeline becomes eligible for garbage collection.