Transforming Our Data
Learn how to use small, composable, and pure functions to transform our individual StatsResult elements to an output useful to us.
We'll cover the following...
The DataPoints
interface
If everything goes well, we now have a TaskEither returning a StatsResult array, with each element of the array containing information for a different type of metric (one will, for example, contain data for errors). We’re ignoring the error path for now. So, how do we transform our individual StatsResult
elements to an output useful to us (a developer keeping an eye on their applications)? Simple: we’ll use small, composable, and pure functions. We already have a folder and file called transformers, so why not add it all in there?
First, we want the total sum of every individual metric. For clarity’s sake, we’ll dissect the AWS Datapoints
type we’re currently returning within StatsResult
:
export type Datapoints = Datapoint[];export interface Datapoint {Timestamp?: Timestamp;Sum?: DatapointValue;Average?: DatapointValue;// other values};export type DatapointValue = number;
Note: If a library has types that it wants to expose to its users, these are typically contained inside a file ending with d.ts. This provides the name of the Cloudwatch types, say
cloudwatch.d.ts
.
Our Datapoints
value is an array of Datapoint.
Each Datapoint contains the Sum
information we want to extract, which is a DatapointValue
, itself an alias for number
. Note that because we set the Period
parameter quite high early on, it’s possible that there’s only one number present within the array. This could help us avoid a lot of work. We can’t be sure of this, however, and assuming this status might create bugs if another user changes either the period or timeframe. We’re assuming it to be a few hours, but what if someone changes it to a few days? In that case, the probability of an array with multiple elements is very high. So, we’ll assume that there might be more than one Datapoint
and thus more than one Sum
.
Now that we know what our type looks like, we add a new type that we want to create out of our current ones:
export type StatsSumResult = {readonly functionName: string,readonly metric: Metric,readonly sum: number,}
As we can see, types.ts
is the same as before, but now the sum is just one number. We should write a function that consolidates Sum
elements into a single number.
import {fold, monoidSum} from "fp-ts/Monoid";export const statsResultToStatsSumResult =(results: readonly StatsResult[]): StatsSumResult[] => {return results.map(b => ({functionName: b.functionName,metric: b.metric,sum: fold(monoidSum)(b.data.map(d => d.Sum)),}));};
This is a seemingly simple function, Because it mostly just maps our current array to a new one of type StatsSumResult
. We might ask what a fold is, though.
What is monoidSum
? Is that similar to a monad?
Remember the semigroup from an earlier chapter? As a quick reminder, a semigroup provides a way of combining two values of the same type, resulting in a single value of that type. A product, which combines ...