Functional Patterns for the non-mathematician
-
Upload
brian-lonsdorf -
Category
Technology
-
view
2.213 -
download
0
description
Transcript of Functional Patterns for the non-mathematician
Functional Patternsfor the non-mathematician
add(4, 2)
//=> 6
// associativeadd(add(1, 2), 4) == add(1, add(2, 4))
// commutativeadd(4, 1) == add(1, 4)
// identityadd(n, 0) == n
// distributivemultiply(2, add(3,4)) == add(multiply(2, 3), multiply(2, 4))
add(4.4, 2.2)
//=> 6.6
var inc = new Increaser(4);inc.increaseBy(2);inc.value();// 6
Interfaces
Properties/Laws
Polymorphic
Composable
Currying
var reverseCap = compose(capitalize, reverse)
reverseCap(“hello”)//=> “Olleh”
Composition
var reverseCap = compose(capitalize, reverse)
reverseCap(“hello”)//=> “Olleh”
Composition
var reverseCap = compose(capitalize, reverse)(“hello”)//=> “Olleh”
Composition“hello”
“olleh”
“Olleh”
compose(compose(f, g), h) == compose(f, compose(g, h))
Composition(associativity)
compose(f, g, h)
Composition(associativity)
var i = compose(g, h)compose(f, i)
Composition(associativity)
var getFromDb = compose(pluck('rows'), User.findAll)
var cleanUpData = compose(capitalize, pluck('name'))
var renderTemplate = TemplateEngine.render(‘users_table')
var makePage = compose(renderTemplate, map(cleanUpData), getFromDb)
makePage({limit: 20})
var getFromDb = compose(pluck('rows'), User.findAll)
var cleanUpData = compose(capitalize, pluck('name'))
var renderTemplate = TemplateEngine.render(‘users_table')
var makePage = compose(renderTemplate, map(cleanUpData), getFromDb)
makePage({limit: 20})Perfect world
function (property, x) { return x[property];}
Getters/Setters
function (property, value, x) { x[property] = value; return x;}
Lenses
over(l, f, x)
view(l, x)
set(l, y, x)
var user = {id: 1, name: ‘Alicia'}
var L = makeLenses([‘name’])
view(L.name, user) // 'Alicia'
set(L.name, 'Ally', user) // {id: 1, name: 'Ally'}
over(L.name, toUpperCase, user) // {id: 1, name: 'ALICIA'}
Lenses
var user = {id: 1, name: {first: ‘doris’, last: ‘day’ }}
var L = makeLenses([‘name’, ‘first’])
var firstNameChar = compose(L.name, L.first, _1)
over(firstNameChar, toUpperCase, user)//=> {id: 1, name: {first: ‘Doris’, last: ‘day’ }}
Lenses
view(l, set(l, b, a)) == b
set(l, view(l, a), a) == a
set(l, c, set(l, b, a)) == set(l, c, a)
Lens laws
if(x !== null && x !== undefined) { return f(x)}
Null checking
fmap(f, Maybe(x))
Null checking
var fmap = function(f, mappable) { return mappable.map(f)}
Null checking
fmap(function(x) { return x.toUpperCase() }, Maybe(‘hi’))//=> Maybe(‘HI’)
fmap(function(x) { return toUpperCase(x); }, Maybe(null))//=> Maybe(null)
Null checking
fmap(function(x) { return x.toUpperCase() }, Maybe(‘hi’))//=> Maybe(‘HI’)
fmap(function(x) { return x.toUpperCase() }, Maybe(null))//=> Maybe(null)
Null checking
compose(fmap(f), Maybe)
Null checking
var id = function(x) { return x; }
fmap(id, x) == id(x)
Fmap laws(identity)
compose(fmap(f), fmap(g)) == fmap(compose(f, g))
Fmap laws(composition)
if(x !== null && x !== undefined) { return f(x)} else { throw ‘Some Error!’}
Error Handling
Error Handling
fmap(f, Either(‘Some error’, x))
Either(‘need an int’, 3)//=> Right(3)
fmap(function(x) { return x + 1; }, Either(‘need an int’, undefined))//=> Left(‘need an int’)
Error Handling
Either(‘need an int’, 3)//=> Right(3)
Either(‘need an int’, undefined))//=> Left(‘need an int’)
Error Handling
fmap(function(x) { return x + 1; }, Right(2))//=> Right(3)
fmap(function(x) { return x + 1; }, Either(‘need an int’, undefined))//=> Left(‘need an int’)
Error Handling
fmap(function(x) { return x + 1; }, Right(2))//=> Right(3)
fmap(function(x) { return x + 1; }, Left(‘need an int’))//=> Left(‘need an int’)
Error Handling
compose(fmap(f), Either(‘error’))
Error Handling
f(x, function(y) {return g(y);
});
Future values
Future values
fmap(f, Promise(x))
var p = new Promise();fmap(function(x) { return log(reverse(x)) }, p)//=> Promise()
p.resolve([1,2,3])//=>[3, 2, 1]
Future values
Something that implements map
Functor
if(x !== null && x !== undefined) { var y = f(x) if(y !== null && y !== undefined) { return g(y) }}
Nesting
f(x, function(y) {return g(y, function(z) {
return h(z)})
})
Nesting
compose(mjoin, fmap(f))
Nesting
var getField = compose(Maybe, document.querySelector)var getValue = compose(Maybe, pluck(‘value’))
var greet = compose(fmap(fmap(concat(‘hello’))), fmap(getValue), getField)greet(‘#name’)//=> Maybe(Maybe(‘hello chris’))
var greet = compose(fmap(concat(‘hello’)), mjoin, fmap(getValue), getField)greet(‘#name’)//=> Maybe(‘hello chris’)
Nesting
var getField = compose(Maybe, document.querySelector)var getValue = compose(Maybe, pluck(‘value’))
var greet = compose(fmap(fmap(concat(‘hello’))), fmap(getValue), getField)greet(‘#name’)//=> Maybe(Maybe(‘hello chris’))
var greet = compose(fmap(concat(‘hello’)), mjoin, fmap(getValue), getField)greet(‘#name’)//=> Maybe(‘hello chris’)
Nesting
compose(mjoin, fmap(g), mjoin, fmap(f))
Nesting
mcompose(g, f)
compose(mjoin, fmap(g), mjoin, fmap(f))
Nesting
mcompose(g, f)
mcompose(mcompose(f, g), h) == mcompose(f, mcompose(g, h))
mcompose(f, M) == f
mcompose(M, f) == f
Monad laws
Multiple null argsvar notNull = function(x) { return x !== null && x !== undefined}
if(notNull(x) && notNull(y)) { return f(x, y)}
Multiple Async fn’svar y,z;
f(x, function(result) { y = result;
if(z) {return h(y, z)
})})
g(x, function(result) { z = result;
if(y) {return h(y, z)
})})
liftA2(f, A(x), A(y))
Multiple values
liftA3(f, A(x), A(y), A(z))
Multiple values
liftA2(add, Maybe(3), Maybe(4))//=> Maybe(7)
liftA2(add, Maybe(null), Maybe(4))//=> Maybe(null)
Multiple values
liftA2(add, Maybe(3), Maybe(4))//=> Maybe(7)
liftA2(add, Maybe(null), Maybe(4))//=> Maybe(null)
Multiple values
var tweets_p = Http.get(‘/twitter/tweets’)var photos_p = Http.get(‘/flickr/photos’)var makeCollage = _.curry(function (tweets, photos){})
liftA2(makeCollage, tweets_p, photos_p)
Multiple values
// identityap(A(id), m) == m
// compositionap(ap(ap(A(compose), f), g), w) == ap(f, ap(g, w))
// homomorphismap(A(f), A(x)) == A(f(x))
// interchangeap(u, A(x)) == ap(A(function(f) { return f(x); }), u)
Applicative laws
Accumulation
reduce(function(acc, x) {return acc + x;
}, 0, [1,2,3])
Accumulation
reduce(function(acc, x) {return acc * x;
}, 1, [1,2,3])
Accumulation
reduce(function(acc, x) {return acc || x;
}, false, [false, false, true])
Accumulation
reduce(function(acc, x) {return acc && x;
}, true, [false, false, true])
Accumulation
reduce(function(acc, x) {return acc > x ? acc : x;
}, 0, [12, 5, 35])
Monoid
mappend(m, m)
mempty(m)
mconcat([m])
Monoid
mappend(m, m)
mempty(m)
mconcat([m])
mconcat([Sum(1), Sum(2), Sum(3)])
//=> Sum(6)
Accumulation
mconcat([Product(1), Product(2), Product(3)])
//=> Product(6)
Accumulation
mconcat([Max(13), Max(2), Max(9)])//=> Max(13)
Accumulation
mconcat([Any(false), Any(false), Any(true)])//=> Any(true)
Accumulation
mconcat([All(false), All(false), All(true)])//=> All(false)
Accumulation
compose(mconcat, map(M))
Accumulation
// left identitymappend(mempty, x) == x
// right identitymappend(x, mempty) == x
// associativitymappend(mappend(x, y), z) == mappend(x, mappend(y,
z))
Monoid laws
Combinators
function(x) { return [f(x), g(x)]}
Combinators
function(x, y) { return [f(x), g(y)]}
compose(f, g)
ampersand(f, g)
asterisk(f, g)
first(f)
second(f)
Arrows
first(reverse)([‘Stan’, ‘Lee']) // [‘natS’, ‘Lee’]
second(reverse)([‘Stan’, ‘Lee']) // [‘Stan’, ‘eeL’]
ampersand(reverse, toUpperCase)(‘Stan’) // [‘natS’, ‘STAN’]
asterisk(reverse, toUpperCase)([‘Stan’, ‘Lee']) // [‘natS’, ‘LEE’]
Arrows
A(id) == id
A(compose(f, g)) == A(compose(f, A(g)))
first(A(f)) == A(first(f))
first(compose(f, g)) == compose(first(f), first(g))
compose(first(f), A(pluck(0))) == compose(A(pluck(0)), f)
compose(first(f), A(asterisk(id, g)) == compose(A(asterisk(id, g)), first(f))
compose(first(first(f)), A(assoc) == compose(A(assoc), first(f))
Arrow laws
Thanks!
@drboolean
https://github.com/DrBoolean/patterns_talk