Currying is a common approach when writing software, but it is unfortunately not as well understood from a practical perspective as Classes. You may have learned about it at University, but not understood the practical benefits. In practical web applications projects I see it being used too little.

Abstracting common functionality in separate locations has several benefits. Perhaps the biggest practical benefit is to get high test coverage for commonly used functionality. If the same logic is coded over and over the code coverage is bound to be low and bugs creep in.

Currying is a way to take a function that describes how to do something and splitting it in the part that must be done once in preparation and the part that must be done every time. If you are in thinking of creating an ad hoc class instance with a do-it method, you probably want use a currying function instead.

Take a simple setup where the function deliverResults must clean up a list of Data by cross-referencing it with an additional reference.

type ID = string;

interface Data {
    id: ID;
    value1;
    value2;
}

interface Ref {
    items: {ids: ID[]}[];
}

function deliverResults(raw: Data[], reference: Ref) {
    return raw.map((entry) => {
        const {id} = entry;
        let found = false;
        for(let item of reference.items) {
            for(let itemId of item.ids) {
                if (itemId === id) {
                    found = true;
                }
            }
        }
        return found ? entry : null;
    }).filter(entry => entry);
}



   In this example if you run deliver results with a large structure it can take significant time.

   With three nested loops 100 x 100 x 100 become 1 million iterations.


   This can be improved by using function currying.

   It could also be done using classes, but the lifecycle of the class instances would be less clear

   and the amount of code needed larger.


   The "Curry" in "Currying" is a reference to logician Haskell Curry, who used the concept extensively,

   but Moses Schönfinkel had the idea 6 years before Curry. It can be traced back to 1893.


   Currying also allows turning multi-parameter functions into single parameter functions, great

   for streaming calls and configuration values.


   It gives certain opportunities to generalise common functional patters loosening the types and

   decomposing the functional flow.

interface WithID {
    id: ID;
}

function alignDataToReference(reference: Ref): (entry: Dta) => Dta | null {
    // This gets called once on setup
    const knownIDs: Set = reference.items.reduce((known, item) => {
        for(let itemId of item.ids) {
            known.add(itemId);
        }
        return known;
    }, new Set())

    // This gets called every time
    return (entry: Dta) => knownIDs.has(entry.id) ? entry : null;
}


   Now that the ability to align against a reference structure is already defined the function can be used to apply to our Data list.

function deliverResultsWithCurry(raw: Data[], reference: Ref) {
    return raw.map(alignDataToReference(reference)).filter(entry => entry);
}






Configuration

Configuration objects are a way to do declarative programming within imperative programming languages. Here simple functions that follow specific patterns are a common and useful pattern.

function textFormatter(value: any) {
    return String(value);
}

const config = [
    {
        field: 'a',
        formatter: textFormatter,
    },
    {
        field: 'b',
        formatter: textFormatter,
    },
];




   Since functions are first order objects they can be used as values in configurations.

   In order to provide related data to the currying can be used.

function prefixedFormatter(prefix: string): (value: any) => string {
    return (value: any) => `${prefix} ${String(value)}`;
}

const config2 = [
    {
        field: 'a',
        formatter: prefixedFormatter('CHF'),
    },
    {
        field: 'b',
        formatter: prefixedFormatter('USD'),
    },
];