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.'
};
}
}
Copied from: http://slides.com/corybrown/functional-style-js
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,056