Functional Patterns for the non-mathematician

83
Functional Patterns for the non-mathematician

description

Fluentconf 2014 talk: Functional design patterns such as lenses, arrows, functors, and monads all come from category theory. To fully grok them, you’ll probably have to wade through the whitest white papers, fighting the mathematical syntax and abstract examples. I’m hoping to demonstrate the ideas into javascript. I’ll be showing direct and practical applications for every day programming.

Transcript of Functional Patterns for the non-mathematician

Page 1: Functional Patterns for the non-mathematician

Functional Patternsfor the non-mathematician

Page 2: Functional Patterns for the non-mathematician

add(4, 2)

//=> 6

Page 3: Functional Patterns for the non-mathematician

// 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))

Page 4: Functional Patterns for the non-mathematician

add(4.4, 2.2)

//=> 6.6

Page 5: Functional Patterns for the non-mathematician

var inc = new Increaser(4);inc.increaseBy(2);inc.value();// 6

Page 6: Functional Patterns for the non-mathematician

Interfaces

Properties/Laws

Polymorphic

Composable

Page 7: Functional Patterns for the non-mathematician

Currying

Page 8: Functional Patterns for the non-mathematician

var reverseCap = compose(capitalize, reverse)

reverseCap(“hello”)//=> “Olleh”

Composition

Page 9: Functional Patterns for the non-mathematician

var reverseCap = compose(capitalize, reverse)

reverseCap(“hello”)//=> “Olleh”

Composition

Page 10: Functional Patterns for the non-mathematician

var reverseCap = compose(capitalize, reverse)(“hello”)//=> “Olleh”

Composition“hello”

“olleh”

“Olleh”

Page 11: Functional Patterns for the non-mathematician

compose(compose(f, g), h) == compose(f, compose(g, h))

Composition(associativity)

Page 12: Functional Patterns for the non-mathematician

compose(f, g, h)

Composition(associativity)

Page 13: Functional Patterns for the non-mathematician

var i = compose(g, h)compose(f, i)

Composition(associativity)

Page 14: Functional Patterns for the non-mathematician

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})

Page 15: Functional Patterns for the non-mathematician

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

Page 16: Functional Patterns for the non-mathematician
Page 17: Functional Patterns for the non-mathematician

function (property, x) { return x[property];}

Getters/Setters

function (property, value, x) { x[property] = value; return x;}

Page 18: Functional Patterns for the non-mathematician

Lenses

over(l, f, x)

view(l, x)

set(l, y, x)

Page 19: Functional Patterns for the non-mathematician

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

Page 20: Functional Patterns for the non-mathematician

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

Page 21: Functional Patterns for the non-mathematician

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

Page 22: Functional Patterns for the non-mathematician
Page 23: Functional Patterns for the non-mathematician

if(x !== null && x !== undefined) { return f(x)}

Null checking

Page 24: Functional Patterns for the non-mathematician

fmap(f, Maybe(x))

Null checking

Page 25: Functional Patterns for the non-mathematician

var fmap = function(f, mappable) { return mappable.map(f)}

Null checking

Page 26: Functional Patterns for the non-mathematician

fmap(function(x) { return x.toUpperCase() }, Maybe(‘hi’))//=> Maybe(‘HI’)

fmap(function(x) { return toUpperCase(x); }, Maybe(null))//=> Maybe(null)

Null checking

Page 27: Functional Patterns for the non-mathematician

fmap(function(x) { return x.toUpperCase() }, Maybe(‘hi’))//=> Maybe(‘HI’)

fmap(function(x) { return x.toUpperCase() }, Maybe(null))//=> Maybe(null)

Null checking

Page 28: Functional Patterns for the non-mathematician

compose(fmap(f), Maybe)

Null checking

Page 29: Functional Patterns for the non-mathematician

var id = function(x) { return x; }

fmap(id, x) == id(x)

Fmap laws(identity)

Page 30: Functional Patterns for the non-mathematician

compose(fmap(f), fmap(g)) == fmap(compose(f, g))

Fmap laws(composition)

Page 31: Functional Patterns for the non-mathematician
Page 32: Functional Patterns for the non-mathematician

if(x !== null && x !== undefined) { return f(x)} else { throw ‘Some Error!’}

Error Handling

Page 33: Functional Patterns for the non-mathematician

Error Handling

fmap(f, Either(‘Some error’, x))

Page 34: Functional Patterns for the non-mathematician

Either(‘need an int’, 3)//=> Right(3)

fmap(function(x) { return x + 1; }, Either(‘need an int’, undefined))//=> Left(‘need an int’)

Error Handling

Page 35: Functional Patterns for the non-mathematician

Either(‘need an int’, 3)//=> Right(3)

Either(‘need an int’, undefined))//=> Left(‘need an int’)

Error Handling

Page 36: Functional Patterns for the non-mathematician

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

Page 37: Functional Patterns for the non-mathematician

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

Page 38: Functional Patterns for the non-mathematician

compose(fmap(f), Either(‘error’))

Error Handling

Page 39: Functional Patterns for the non-mathematician
Page 40: Functional Patterns for the non-mathematician

f(x, function(y) {return g(y);

});

Future values

Page 41: Functional Patterns for the non-mathematician

Future values

fmap(f, Promise(x))

Page 42: Functional Patterns for the non-mathematician

var p = new Promise();fmap(function(x) { return log(reverse(x)) }, p)//=> Promise()

p.resolve([1,2,3])//=>[3, 2, 1]

Future values

Page 43: Functional Patterns for the non-mathematician

Something that implements map

Functor

Page 44: Functional Patterns for the non-mathematician
Page 45: Functional Patterns for the non-mathematician

if(x !== null && x !== undefined) { var y = f(x) if(y !== null && y !== undefined) { return g(y) }}

Nesting

Page 46: Functional Patterns for the non-mathematician

f(x, function(y) {return g(y, function(z) {

return h(z)})

})

Nesting

Page 47: Functional Patterns for the non-mathematician

compose(mjoin, fmap(f))

Nesting

Page 48: Functional Patterns for the non-mathematician

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

Page 49: Functional Patterns for the non-mathematician

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

Page 50: Functional Patterns for the non-mathematician

compose(mjoin, fmap(g), mjoin, fmap(f))

Nesting

mcompose(g, f)

Page 51: Functional Patterns for the non-mathematician

compose(mjoin, fmap(g), mjoin, fmap(f))

Nesting

mcompose(g, f)

Page 52: Functional Patterns for the non-mathematician

mcompose(mcompose(f, g), h) == mcompose(f, mcompose(g, h))

mcompose(f, M) == f

mcompose(M, f) == f

Monad laws

Page 53: Functional Patterns for the non-mathematician
Page 54: Functional Patterns for the non-mathematician

Multiple null argsvar notNull = function(x) { return x !== null && x !== undefined}

if(notNull(x) && notNull(y)) { return f(x, y)}

Page 55: Functional Patterns for the non-mathematician

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)

})})

Page 56: Functional Patterns for the non-mathematician

liftA2(f, A(x), A(y))

Multiple values

Page 57: Functional Patterns for the non-mathematician

liftA3(f, A(x), A(y), A(z))

Multiple values

Page 58: Functional Patterns for the non-mathematician

liftA2(add, Maybe(3), Maybe(4))//=> Maybe(7)

liftA2(add, Maybe(null), Maybe(4))//=> Maybe(null)

Multiple values

Page 59: Functional Patterns for the non-mathematician

liftA2(add, Maybe(3), Maybe(4))//=> Maybe(7)

liftA2(add, Maybe(null), Maybe(4))//=> Maybe(null)

Multiple values

Page 60: Functional Patterns for the non-mathematician

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

Page 61: Functional Patterns for the non-mathematician

// 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

Page 62: Functional Patterns for the non-mathematician
Page 63: Functional Patterns for the non-mathematician

Accumulation

reduce(function(acc, x) {return acc + x;

}, 0, [1,2,3])

Page 64: Functional Patterns for the non-mathematician

Accumulation

reduce(function(acc, x) {return acc * x;

}, 1, [1,2,3])

Page 65: Functional Patterns for the non-mathematician

Accumulation

reduce(function(acc, x) {return acc || x;

}, false, [false, false, true])

Page 66: Functional Patterns for the non-mathematician

Accumulation

reduce(function(acc, x) {return acc && x;

}, true, [false, false, true])

Page 67: Functional Patterns for the non-mathematician

Accumulation

reduce(function(acc, x) {return acc > x ? acc : x;

}, 0, [12, 5, 35])

Page 68: Functional Patterns for the non-mathematician

Monoid

mappend(m, m)

mempty(m)

mconcat([m])

Page 69: Functional Patterns for the non-mathematician

Monoid

mappend(m, m)

mempty(m)

mconcat([m])

Page 70: Functional Patterns for the non-mathematician

mconcat([Sum(1), Sum(2), Sum(3)])

//=> Sum(6)

Accumulation

Page 71: Functional Patterns for the non-mathematician

mconcat([Product(1), Product(2), Product(3)])

//=> Product(6)

Accumulation

Page 72: Functional Patterns for the non-mathematician

mconcat([Max(13), Max(2), Max(9)])//=> Max(13)

Accumulation

Page 73: Functional Patterns for the non-mathematician

mconcat([Any(false), Any(false), Any(true)])//=> Any(true)

Accumulation

Page 74: Functional Patterns for the non-mathematician

mconcat([All(false), All(false), All(true)])//=> All(false)

Accumulation

Page 75: Functional Patterns for the non-mathematician

compose(mconcat, map(M))

Accumulation

Page 76: Functional Patterns for the non-mathematician

// left identitymappend(mempty, x) == x

// right identitymappend(x, mempty) == x

// associativitymappend(mappend(x, y), z) == mappend(x, mappend(y,

z))

Monoid laws

Page 77: Functional Patterns for the non-mathematician
Page 78: Functional Patterns for the non-mathematician

Combinators

function(x) { return [f(x), g(x)]}

Page 79: Functional Patterns for the non-mathematician

Combinators

function(x, y) { return [f(x), g(y)]}

Page 80: Functional Patterns for the non-mathematician

compose(f, g)

ampersand(f, g)

asterisk(f, g)

first(f)

second(f)

Arrows

Page 81: Functional Patterns for the non-mathematician

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

Page 82: Functional Patterns for the non-mathematician

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

Page 83: Functional Patterns for the non-mathematician

Thanks!

@drboolean

https://github.com/DrBoolean/patterns_talk