Abdelrahman Awad
Software Engineer @Rasayel. Open-Source contributor.
Functions in the mathematical sense
A function takes input(s) and definitely always has a return value.
Β f(x) = 2x + 3
function fn(x) {
return 2 * x + 3;
}
function p1() {
return 'something';
}
function p2(x) {
const y = x + 1;
}
β
π
// unary
function foo(x) {
// ..
}
foo.length; // 1
// binary
function foo(x, y) {
// ..
}
foo.length; // 2
// trinary
function foo(x, y, z) {
// ..
}
foo.length; // 3
var y;
function f(x) {
y = (2 * Math.pow( x, 2 )) + 3;
}
f(2);
y; // 11
function f(x) {
return (2 * Math.pow( x, 2 )) + 3;
}
var y = f( 2 );
y; // 11
function sum(list) {
var total = 0;
for (let i = 0; i < list.length; i++) {
if (!list[i]) list[i] = 0;
total = total + list[i];
}
return total;
}
var nums = [ 1, 3, 9, 27, , 84 ];
sum( nums ); // 124
Is this code problematic? π€
A function with no side causes/effects is called a pure function. A pure function is idempotent in the programming sense, because it cannot have any side effects.
function add(x,y) {
return x + y;
}
const PI = 3.141592;
function circleArea(radius) {
return PI * radius * radius;
}
function cylinderVolume(radius,height) {
return height * circleArea( radius );
}
β
β
Referential transparency is the assertion that a function call could be replaced by its output value, and the overall program behavior wouldnβt change.
Pure functions have referential transparency
Functions of Functions
function forEach(list,fn) {
for (let v of list) {
fn(v);
}
}
forEach([1,2,3,4,5], (val) => {
console.log(val);
});
function foo() {
return function inner(msg) {
return msg.toUpperCase();
};
}
Closure is when a function remembers and accesses variables from outside of its own scope, even when that function is executed in a different scope.
function counter (start) {
var val = start;
return function current(increment = 1){
val = val + increment;
return val;
};
}
const myCounter = counter(0);
myCounter(); // 1
myCounter(); // 2
myCounter(); // 3
function incrementBy (inc) {
return function (value) {
return value + inc;
}
}
const increment = incrementBy(1);
const increment2 = incrementBy(2);
const increment3 = incrementBy(3);
increment(5); // 6
increment2(5); // 7
increment3(5); // 8
Β is the process of taking a function with multiple arguments and returning a series of functions that take one argument and eventually resolve to a value.
function volume(l, w, h) {
return l * w * h;
}
// curried
function volume1(length) {
return function(width) {
return function(height) {
return height * width * length;
}
}
}
volume(2,3,4); // 24
volume1(2)(3)(4); // 24
function curry(fn) {
return (function nextCurried(prevArgs) {
return function curried(nextArg) {
var args = [...prevArgs, nextArg];
if (args.length >= fn.length) {
return fn(...args);
}
return nextCurried(args);
};
})([]);
}
function clap (str) {
return str.split(' ').join(' π ');
}
function scream (str) {
return str.toUpperCase();
}
clap(
scream(
'Hello World!'
) // HELLO WORLD
) // HELLO π WORLD
const louder = compose(clap, scream);
louder('Hello World!') // HELLO π WORLD
function compose2(fn2, fn1) {
return function composed(origValue) {
return fn2(fn1(origValue));
};
}
finalValue <-- func1 <-- func2 <-- ... <-- funcN <-- origValue
What if we want to compose more than two functions?
function compose(...fns) {
return fns.reverse().reduce((fn1,fn2) => {
return function composed(...args){
return fn2( fn1( ...args ) );
};
});
}
Order of operations is not Omni-directional in most cases
function add (x) {
return x + 3;
}
function mul (x) {
return x * 4;
}
const c1 = compose(add, mul);
const c2 = compose(mull, add);
c1(5) !== c2(5);
function pipe(...fns) {
return fns.reduce((fn1,fn2) => {
return function piped(...args){
return fn2( fn1( ...args ) );
};
});
}
function double(x) {
return x * 2;
}
[1,2,3,4,5].map(v => {
return double(v);
});
function double(x) {
return x * 2;
}
[1,2,3,4,5].map(double);
["1","2","3"].map(v => {
return parseInt(v);
});
["1","2","3"].map(parseInt);
β¬
π
function unary (fn) {
return function (arg) {
return fn(arg);
}
}
Locks the arity to 1
["1","2","3"].map(unary(parseInt));
Immutability plays a large role when building pure functions
function reverse (arr) {
return [...arr].reverse();
}
let arr = [1, 2, 3, 4, 5];
reverse(arr);
function reverse (arr) {
return arr.reverse();
}
let arr = [1, 2, 3, 4, 5];
reverse(arr);
function process (obj) {
const newObj = { ...obj };
// DO stuff
return newObj;
}
const obj = {
a: 12,
b: 'hello world',
c: { x: 0, y: 10 }
};
process(obj);
function process (obj) {
const newObj = { ...obj, c: { ...obj.c } };
// DO stuff
return newObj;
}
const obj = {
a: 12,
b: 'hello world',
c: { x: 0, y: 10 }
};
process(obj);
Deep
import produce from "immer"
const baseState = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
];
const nextState = produce(baseState, draftState => {
draftState.push({todo: "Tweet about it"})
draftState[1].done = true
});
This Presentation is based on examples and explanations from Functional Lite JavaScriptΒ by Kyle Simpson
By Abdelrahman Awad