Practical Functional Programming in CFML

By Abram Adams

Adams Family

About Me

  • Dropped out of high skul as a freshman...
  • Began developing software in 1999 (VB 6)
    (well 1997 if you count Excel/Access)
  • Developing ColdFusion applications since 2000
  • Have held many titles from:
    support tech, to dev, to architect, to CTO
  • Creator/maintainer of trycf.com

Key Takeaways

  • Basic understand of what Functional Programming (FP) is
  • Demystify some key FP terms:
    • Side-Effects
    • Pure Functions
    • Referential Transparency
    • Immutability
    • Higher-Order Functions
    • Lambdas
    • Currying
    • Lazy Evaluation
    • Memoization
  • How these things apply to CFML

CFML
IS NOT A
PURE FUNCTIONAL
LANGUAGE

CFML
IS NOT A
PURE OBJECT ORIENTED

LANGUAGE

CFML
IS A DYNAMIC

MULTI-PARADIGM

LANGUAGE

WHAT IS FUNCTIONAL PROGRAMMING

Art of avoiding side-effects using concepts such as pure functions and immutable data

Side Effects

Side Effects

When a procedure changes a variable from outside its scope

Don't Do

function build(struct data){
    data["a"] = "Surprise!";
}

Do Do

function build(struct data){
    var returnData = duplicate( data );
    returnData["a"] = "No Surprises!";
    return returnData;
}

Immutable

Unchanging over time or unable to be changed​

 

CF !

// Instructor: What's 5 plus 2?
numberFive().plus(numberTwo());
// Student: 7?
// Instructor: Correct! What's 5 plus 3?
// Student: 8?
// Instructor: Wrong! It's 10, 
// because we turned 5 into 7, remember?

Subtle Difference Between Mutating and Replacing Data

Immutable Techniques

Arrays

Structs

Wish We Could Do

a = ["Some value","Another"];

b = [ ...a, addSomething() ];

function addSomething( ){
    return "Something";
}
//OUTPUT
// a: "Some value", "Another"
// b: "Some value", "Another", "Something"

Wish We Could Do

a = {"product": "Jelly"};

b = {"price":"5.00"};

c = { ...a, ...b };

//OUTPUT
// c = {"product": "Jelly","price":"5.00"}

Bring Spread Operator and Destructuring to CFML

Please Vote :)

Adobe

Lucee

BIFs That Mutate State

  • ArrayAppend
  • ArrayPrepend
  • ArrayUpdate
  • ArrayInsert
  • ArrayInsertAt
  • ArrayDelete
  • ArrayDeleteAt
  • ArraySort
  • ArraySet
  • ArraySwap
  • StructAppend
  • StructInsert
  • StructClear
  • StructDelete

BIFs That Enforce Immutability

  • ArrayNew
  • *Map
  • *Reduce
  • *Filter
  • ArraySlice
  • QuerySlice (Lucee Only)
  • ArrayReverse (Lucee Only)
  • StructNew
  • StructCopy

* Array, Struct, List and Query versions

Pure Functions

Given the same inputs will always return the same output

Pure Functions

Don't have external dependencies

Referential Transparency

an expression may be replaced by its value without changing the result of the program

function nameIsBob(name){
  return name == "bob";
}

if( nameIsBob("bob") ){
  // do stuff
};

// SAME AS
if( true ){
  // do stuff
}

Pure Functions

The only observable output is the return value.

NEVER OUTPUT DATA IN A FUNCTION

HIGHER ORDER FUNCTIONS

Any function which either accepts a function as an argument, or returns a function.

Takes A Function

Returns A Function

Nested Function He..ck

Lambdas

(AKA: Arrow Functions)

Nested Function Heaven

Filter

Map

Reduce

Filter

Creates an array/struct/list/query filled with all elements that pass a test (provided as a function)

Filter - ArrayFilter

Map

"applies a given function to each element of a functor" -Wikipedia

                 array/struct/list/query

Map - ArrayMap

Reduce

The reduce() method executes a reducer function (that you provide) on each element of the array/struct/list/query, resulting in a single output value.

Reduce - ArrayReduce

Chaining Higher-Order Functions

Lazy

Lazy

A strategy which holds the evaluation of an expression until its value is needed

Lazy - Use Case

Memoization

memoization is an optimization technique to store pre-computed results (cache), to avoid recomputation for the same inputs.

Memoization can only work if the function is Pure and "Referentially Transparent"

Lucee Only

Currying

Function Arity

(Only One Argument)

function makeRequest(appKey, user, url) {

    if (validateAppKey(appKey)) {

        if (authorizedUser(user)) {

            return request(url);

        } else { 
            return { 
                statusCode: 401, 
                message: 'user not authorized' 
            };
        }
    
    } else {
        return {
            statusCode: 401,
            message: 'app not authorized.'
        };
    }

}
makeRequest = (appKey) => (user) => (url) => {

    if (validateAppKey(appKey)) {

        if (authorizedUser(user)) {

            return request(url);

        } else { 
            return { 
                statusCode: 401, 
                message: 'user not authorized' 
            };
        }
    
    } else {
        return {
            statusCode: 401,
            message: 'app not authorized.'
        };
    }

}
getData = makeRequest('abc')(session.user)('https://myapi.io');

Curried

getData = makeRequest('abc', session.user, 'https://myapi.io');
// Same as
getData = makeRequest('abc', session.user)('https://myapi.io');
// Same as
getData = makeRequest('abc')( session.user, 'https://myapi.io');

Functional Languages

Function Composition

Combining two or more functions to create a new more specialized function

makeApiRequest = makeRequest('abc');

makeApiRequestAsUser = makeAppRequest(session.user);

makeApiRequestAsUser('https://myapi.io');

Composed

Composed - Specialized

The End

 Resources

Practical Functional Programming in CFML

By Abram Adams

Practical Functional Programming in CFML

  • 2,070