Jesús García
// impure
const add = (x, y) => {
launchMissiles()
return x + y
}
// pure
const add = (x, y) => x + y
Purity
const accounts = [{
amount: 10,
currency: 'USD'
}, {
amount: 20,
currency: 'EUR'
}]
const newAccount = {
amount: 3,
currency: 'MXN'
}
// mutable
accounts.push(newAccount)
//immutable
newAccounts = accounts.concat(newAccount)
Immutability
High level
Low level
// imperative
var accounts = [{
amount: 1234,
currency: EUR
}, {
amount: 6578,
currency: EUR
}, {
amount: 6234,
currency: USD
}
]
var totalEur = 0
for (var i = 0; i < accounts.length; i++) {
if (account.currency === 'EUR'){
totalEur += account.amount
}
}
High level
// functional
accounts
.filter(account =>
account.currency === 'EUR')
.reduce((total, account) =>
total + account.amount)
// functional (refactor)
const add = (x, y) => x + y
const prop = prop => obj => obj[prop]
const propEq = (prop, value) => obj => obj[prop] === value
accounts
.filter(propEq('currency', currency)
.map(prop('amount'))
.reduce(add, 0)
Lazyness
Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate.
// aplicación parcial
var add = function(a) {
return function(b) {
return a + b
}
}
// para funciones binarias se puede hacer:
var add = function(a, b) {
if (b === undefined) {
return function(b) {
return a + b
}
}
return a + b
}
// con la función 'curry'
var add = curry(function(a, b) {
return a + b
})
// con arrow functions
const add = a => b => a + b
// implicit return
const shout = string => string.toUppercase() + '!!!'
// no implicit return
const foo = (arg1, arg2) => {
// do stuff
// return something
}
// return an object by wrapping it in parentheses
const makeCar = wheels => ({
wheels: wheels
})
// wrap any expression in parenthesis for implicit return
const sanitize = string => (
string
.trim()
.toUpperCase()
)
//(binary) compose
const compose = f => g => x => f(g(x))
const upperCase = s => s.toUpperCase()
const trim = s => s.trim()
const sanitize = compose(
upperCase,
trim
)
//pipe is a variadic compose
//that goes from left to right
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x)
const inc = x => x + 1
const square = x => x * x
const timesTen = x => x * 10
const operations = pipe(
inc,
square,
timesTen
)
const arr = [1,2,3]
arr.map(operations) //[40, 90, 160]
const Id = v =>
({
value: v,
map: f => Id(f(v))
})
//Array is a functor
['hello']
.map(str => str.length)
.map(n => n < 5)
[0]
//map delegates to the implementation
//of the specific functor
const map = fn => functor =>
functor.map(fn)
u.map(a => a) is equivalent to u (identity)
u.map(x => f(g(x))) is equivalent to u.map(g).map(f) (composition)
map :: Functor f => f a ~> (a -> b) -> f b
const Sum = x =>
({
x
, concat: ({x: y}) =>
Sum(x + y)
, empty: _ => Sum(0)
})
const Product = x =>
({
x
, concat: ({x: y}) =>
Product(x * y)
, empty: _ => Sum(1)
})
a.concat(b).concat(c) is equivalent to a.concat(b.concat(c)) (associativity)
concat :: Monoid a => a ~> a -> a
m.concat(M.empty()) is equivalent to m (right identity)
M.empty().concat(m) is equivalent to m (left identity)
empty :: Monoid m => () -> m
var xLens = lens(prop('x'), assoc('x'));
view(xLens, {x: 1, y: 2}); //=> 1
set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}
over(xLens, R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}
// refactor pipe with combinators
const apply = (f, x) => f(x)
const flip = f => (a, b) => f(b, a)
const pipe = (...fns) => x => fns.reduce(flip(apply), x)