You will see 
• Lots of functional JavaScript code 
• Benefits and drawbacks 
• Functional Optimizations 
• Lots of unexplained type signatures
You won’t see 
• Detailed explanations of the code 
• Definitions for functional constructs 
• Extensive performance analysis
59 // src :: FlickrItem -> URL 
60 var src = compose(_.get('m'), _.get('media')); 
61 
62 // srcs :: FlickrSearch -> [URL] 
63 var srcs = compose(map(src), _.get('items')); 
64 
65 // images :: FlickrSearch -> [DOM] 
66 var images = compose(map(imageTag), srcs); 
67 
68 // tags :: FlickrSearch -> [DOM] 
69 var tags = compose(toP, _.countBy(_.id), _.filter(_.isEmpty), chain(split(‘ ‘))); 
70 
71 // imagesAndTags :: Tuple [DOM] [DOM] 
72 var imagesAndTags = liftA2(Tuple, images, tags); 
73 
74 // widget :: String -> PictureBox 
75 var widget = compose(PictureBox, map(imagesAndTags), getJSON, url); 
76 
77 /////////////////////////////////////////////////////////////////////////////////// 
78 
79 mconcat([widget('cats'), widget('dogs')]).fork(log, function(x){ 
80 compose(setHtml($('#flickr')), _.first)(x) 
81 compose(setHtml($(‘#tag_cloud')), _.last)(x) 
82 }); 
83 });
style provides benefits. You should do it whenever it is convenient, and
Questions 
• Why might one do this? 
• What problems are there? 
• Is it performant? 
• Is it “production ready?”
Pointfree
We can curry 
var replace = curry(function(regex, x, replaceable) { 
return replaceable.replace(regex, x); 
}); 
var squish = replace(/s+/g, ''); 
squish("I like to move it move it"); // Iliketomoveitmoveit
We can compose 
var wackyText = compose(capitalize, reverse, squish) 
wackyText(“turtle power") // Rewopeltrut
Pointfree 
var clientApp = compose(render, doThings, httpGet(‘/posts')) 
var serverApp = compose(sendJSON, doThings, Db.all(‘posts’)) 
var shellApp = compose(display, doThings, prompt("what's up?"))
Pointfree 
httpGet('/post/2', function(json){ 
renderPost(json); 
});
Pointfree 
httpGet('/post/2', function(json, err){ 
renderPost(json, err); 
});
Pointfree 
httpGet(‘/post/2’, renderPost)
Pointfree 
httpGet(‘/post/2’, renderPost) 
GOOD
Pointfree 
var goodArticles = function(articles) { 
return _.filter(articles, function(article){ 
return _.isDefined(article); 
}) 
}
Pointfree 
var goodArticles = function(articles) { 
return _.filter(articles, function(article){ 
return _.isDefined(article); 
}) 
} 
var goodArticles = filter(isDefined)
Pointfree 
var goodArticles = function(articles) { 
return _.filter(articles, function(article){ 
return _.isDefined(article); 
}) 
} 
var goodArticles = filter(isDefined)
Pointfree 
var goodArticles = function(articles) { 
return _.filter(articles, function(article){ 
return _.isDefined(article); 
}) 
} 
var goodArticles = filter(isDefined) 
GOOD
Pointfree 
var getChildren = function(el) { 
return el.childNodes; 
} 
var getAllChildren = function(els) { 
return _.map(els, function(el) { 
return getChildren(el); 
}); 
}
Pointfree 
var getChildren = function(el) { 
return el.childNodes; 
} 
var getAllChildren = function(els) { 
return _.map(els, function(el) { 
return getChildren(el); 
}); 
} 
var getChildren = get('childNodes') 
var getAllChildren = map(getChildren)
Pointfree 
var getChildren = function(el) { 
return el.childNodes; 
} 
var getAllChildren = function(els) { 
return _.map(els, function(el) { 
return getChildren(el); 
}); 
} 
var getChildren = get('childNodes') 
var getAllChildren = map(getChildren)
Pointfree 
var getChildren = function(el) { 
return el.childNodes; 
} 
var getAllChildren = function(els) { 
return _.map(els, function(el) { 
return getChildren(el); 
}); 
} 
var getChildren = get('childNodes') 
var getAllChildren = map(getChildren) 
GOOD
Pointfree 
var grandChildren = function(selector) { 
var el = document.querySelector(selector); 
var children = getChildren(el); 
return map(getChildren, children); 
} 
var grandChildren = compose( map(getChildren) 
, getChildren 
, document.querySelector 
)
Pointfree 
var grandChildren = function(selector) { 
var el = document.querySelector(selector); 
var children = getChildren(el); 
return map(getChildren, children); 
} 
var grandChildren = compose( map(getChildren) 
, getChildren 
, document.querySelector 
) 
GOOD
Pointfree 
var video = { src: 'http://youtube.com?v=23423' 
, description: 'family matters ep1' 
, screenshots: [ { url: 'i.ytimg.com/OrIxGlo.webp', size: ‘120x120' } 
, { url: 'i.ytimg.com/3rAxRdb.webp', size: ‘1020x764' } 
] 
} 
var thumb = compose(_.first, _.get(‘screenshots')) 
var thumbUrl = compose(_.get('url'), thumb) 
var thumbWithHost = compose(concat('http://youtube.com'), thumbUrl) 
thumbWithHost(video) 
// http://youtube.com/i.ytimg.com/OrIxaBJ9Glo.webp
Pointfree 
var video = { src: 'http://youtube.com?v=23423' 
, description: 'family matters ep1' 
, screenshots: [ { url: 'i.ytimg.com/OrIxGlo.webp', size: ‘120x120' } 
, { url: 'i.ytimg.com/3rAxRdb.webp', size: ‘1020x764' } 
] 
} 
var thumbUrl = compose(_.get('url'), thumb) 
var thumb = compose(_.first, _.get(‘screenshots')) 
var thumbWithHost = compose(concat('http://youtube.com'), thumbUrl) 
thumbWithHost(video) 
// TypeError
Pointfree 
var video = { src: 'http://youtube.com?v=23423' 
, description: 'family matters ep1' 
, screenshots: [ { url: 'i.ytimg.com/OrIxGlo.webp', size: ‘120x120' } 
, { url: 'i.ytimg.com/3rAxRdb.webp', size: ‘1020x764' } 
] 
} 
var thumbUrl = compose(_.get('url'), thumb) 
var thumb = compose(_.first, _.get(‘screenshots')) 
var thumbWithHost = compose(concat('http://youtube.com'), thumbUrl) 
thumbWithHost(video) 
// TypeError 
Meh
First Class? 
["10", "50", "20"].map(function(x){ return parseInt(x); }); 
// [10, 50, 20] 
["10", "50", "20"].map(parseInt); 
// [10, NaN, NaN]
First Class? 
["10", "50", "20"].map(function(x){ return parseInt(x); }); 
// [10, 50, 20] 
["10", "50", "20"].map(parseInt); 
// [10, NaN, NaN] 
// "The Madness of King JavaScript" by Reg Braithwaite (raganwald) 
BAD
First Class? 
var phone = { 
dial: function(x){ console.log("DIALING: ", this.format(x)); }, 
format: function(n) { return n.replace(/-/g, '') } 
}; 
var numbers = ["234-3535-2342", “1-653-124-8321"]; 
numbers.map(function(x){ return phone.dial(x); }); 
// DIALING: 23435352342 
// DIALING: 16531248321
First Class? 
var phone = { 
dial: function(x){ console.log("DIALING: ", this.format(x)); }, 
format: function(n) { return n.replace(/-/g, '') } 
}; 
var numbers = ["234-3535-2342", “1-653-124-8321"]; 
numbers.map(function(x){ return phone.dial(x); }); 
// DIALING: 23435352342 
// DIALING: 16531248321 
numbers.map(phone.dial);
First Class? 
var phone = { 
dial: function(x){ console.log("DIALING: ", this.format(x)); }, 
format: function(n) { return n.replace(/-/g, '') } 
}; 
var numbers = ["234-3535-2342", “1-653-124-8321"]; 
numbers.map(function(x){ return phone.dial(x); }); 
// DIALING: 23435352342 
// DIALING: 16531248321 
numbers.map(phone.dial); 
// TypeError: Object #<Object> has no method 'format'
First Class? 
var phone = { 
dial: function(x){ console.log("DIALING: ", this.format(x)); }, 
format: function(n) { return n.replace(/-/g, '') } 
}; 
var numbers = ["234-3535-2342", “1-653-124-8321"]; 
numbers.map(function(x){ return phone.dial(x); }); 
// DIALING: 23435352342 
// DIALING: 16531248321 
numbers.map(phone.dial.bind(phone)); 
// DIALING: 23435352342 
// DIALING: 16531248321
First Class? 
var phone = { 
dial: function(x){ console.log("DIALING: ", this.format(x)); }, 
format: function(n) { return n.replace(/-/g, '') } 
}; 
var numbers = ["234-3535-2342", “1-653-124-8321"]; 
numbers.map(function(x){ return phone.dial(x); }); 
// DIALING: 23435352342 
// DIALING: 16531248321 
numbers.map(phone.dial.bind(phone)); 
// DIALING: 23435352342 
// DIALING: 16531248321 
BAD
//+ render :: [Tag] -> DOM 
var render = compose($(‘#tag-cloud').html, tagView, addFontSizes) 
//+ tagCloud :: Params -> Future(DOM) 
var tagCloud = compose(map(render), httpGet('/tags'))
//+ setHtml :: String -> Html -> IO(DOM) 
var setHtml = curry(function(selector, h) { 
return IO(function() { return $(selector).html(h); }); 
}); 
//+ render :: [Tag] -> IO(DOM) 
var render = compose(setHtml(‘#tag-cloud'), tagView, addFontSizes) 
//+ tagCloud :: Params -> Future(IO(DOM)) 
var tagCloud = compose(map(render), httpGet('/tags'))
Pointfree 
• Removes unnecessary code 
• High level declarative apps 
• Encourages generic code 
• Encourages purity
Pointfree 
• Order of definition matters 
• Weird interop with imperative community (impure, 
swiss army fn’s, & ‘this’)
Demos
Typeclass
Typeclass 
promise.then(function(x){ return x + 1 }); // Promise(2) 
[1].map(function(x) { return x + 1 }); // [2] 
event_stream.subscribe(function(x) { return x + 1 }) // EventStream(2)
Typeclass 
var Container = function(val) { 
this.val = val; 
} 
Container.prototype.of = function(x) { 
return new Container(x); 
} 
Container.prototype.chain = function(f) { 
return f(this.val); 
};
Typeclass 
We can map over them 
(a -> b) -> M(a) -> M(b)
Typeclass 
We can map over them 
(a -> b) -> M(a) -> M(b) 
promise.map(function(x){ return x + 1 }); // Promise(2) 
[1].map(function(x) { return x + 1 }); // [2] 
event_stream.map(function(x) { return x + 1 }) // EventStream(2) 
Just(1).map(function(x) { return x + 1 }) // Just(2) 
Nothing.map(function(x) { return x + 1 }) // Nothing 
IO(1).map(function(x) { return x + 1 }) // IO(2)
Typeclass 
We can flatten/un-nest them 
M(M(a)) -> M(a)
Typeclass 
We can flatten/un-nest them 
M(M(a)) -> M(a) 
[['hello']] -> ['hello'] 
Just(Just(true)) -> Just(true)
Typeclass 
We can apply functions within them 
M(a -> b) -> M(a) -> M(b)
Typeclass 
We can apply functions within them 
M(a -> b) -> M(a) -> M(b) 
var finished = curry(function(click, anim) { alert(“Finished!"); }); 
EventStream.of(finished).ap(button_clicks).ap(animation_done);
Typeclass 
We can combine them 
M(a) -> M(a) -> M(a)
Typeclass 
We can combine them 
M(a) -> M(a) -> M(a) 
Api.get(‘/unstarred’).concat(Api.get('/starred')) 
// Future([Item])
Typeclass 
We can compose them 
M(N(a)) -> MN(a)
Typeclass 
We can compose them 
M(N(a)) -> MN(a) 
//+ askQuestion :: IO(Maybe(String)) 
compose(map(map(storeResponse)), askQuestion) 
//+ askQuestion :: Compose(IO(Maybe(String))) 
compose(map(storeResponse), Compose, askQuestion)
Typeclass 
We can commute them 
M(N(a)) -> N(M(a))
Typeclass 
We can commute them 
M(N(a)) -> N(M(a)) 
compose(sequenceA, map(readFile)) 
// Future([String])
Typeclass 
We can derive lots of this! 
derive(MyType, [Functor, Applicative, Foldable]) 
*https://github.com/fantasyland/fantasy-land/pull/66
Typeclass 
//+ validate :: UserAttrs -> Either([Error], UserAttrs) 
//+ saveUser :: UserAttrs -> Future(User) 
//+ emailPassword :: User -> Future(Email) 
//+ saveThenEmail :: UserAttrs -> Future(Email) 
var saveThenEmail = compose(chain(emailPassword), saveUser) 
//+ createUser :: UserAttrs -> Either([Error], Future(Email)) 
var createUser = compose(map(saveThenEmail), validate)
Typeclass 
//+ validate :: UserAttrs -> Either([Error], UserAttrs) 
//+ saveUser :: UserAttrs -> Future(User) 
//+ emailPassword :: User -> Future(Email) 
//+ saveThenEmail :: UserAttrs -> Future(Email) 
var saveThenEmail = compose(chain(emailPassword), saveUser) 
//+ createUser :: UserAttrs -> Either([Error], Future(Email)) 
var createUser = compose(map(saveThenEmail), validate) 
GOOD
GOOD
Typeclass 
//+ lookup :: String -> {String: a} -> a|null 
var lookup = curry(function(x, obj) { 
return obj[x]; 
}) 
var upperName = compose(toUpperCase, lookup(‘name')) 
upperName({name: “Tori Amos"}) // “TORI AMOS” 
upperName({first_name: "Tori", last_name: “Spelling"}) // BOOM!
Typeclass 
//+ safeLookup :: String -> {String: a} -> Maybe(a) 
var safeLookup = curry(function(x, obj) { 
return Maybe.fromNullable(obj[x]); 
}); 
var upperName = compose(map(toUpperCase), safeLookup(‘name')) 
upperName({name: “Tori Amos”}) 
// Just(“TORI AMOS”) 
upperName({first_name: "Tori", last_name: “Spelling”}) 
// Nothing
Typeclass 
//+ safeLookup :: String -> {String: a} -> Maybe(a) 
var safeLookup = curry(function(x, obj) { 
return Maybe.fromNullable(obj[x]); 
}); 
var upperName = compose(map(toUpperCase), safeLookup(‘name')) 
upperName({name: “Tori Amos”}) 
// Just(“TORI AMOS”) 
upperName({first_name: "Tori", last_name: “Spelling”}) 
// Nothing 
GOOD
GOOD
Typeclass 
//+ readFile :: Future(Maybe(String)) 
//+ lipogram :: String -> Future(Maybe(String)) 
var lipogram = compose(map(map(replace(/e/ig))), readFile);
Typeclass 
//+ readFile :: String -> Compose(Future(Maybe(String))) 
var readFile = compose(Compose, _readFile) 
//+ lipogram :: String -> Compose(Future(Maybe(String))) 
var lipogram = compose(map(replace(/e/ig)), readFile);
Typeclass 
var WebApp = ReaderT(StateT(Future))
Typeclass 
Meh 
var WebApp = ReaderT(StateT(Future))
Typeclass 
do 
a <- Just 3 
b <- Just 1 
return a + b 
Just(3).chain(function(a) { 
return Just(1).chain(function(b) { 
return Maybe.of(a + b); 
}); 
}; 
Haskell 
JavaScript
Typeclass 
do 
a <- Just 3 
b <- Just 1 
return a + b 
Haskell 
JavaScript liftM2(add, Just(3), Just(1))
Typeclass 
do 
post_id <- param "id" 
post <- lift $ findPost post_id 
return (toJSON post) 
Haskell 
(lift . (fmap toJSON) . findPost) =<< param "id" 
JavaScript 
chain(compose(lift, map(toJSON), findPost), param(‘id'))
Typeclass 
do 
post_id <- param "id" 
post <- lift $ findPost post_id 
return (toJSON post) 
Haskell 
(lift . (fmap toJSON) . findPost) =<< param "id" 
JavaScript 
chain(compose(lift, map(toJSON), findPost), param(‘id')) 
Meh
Typeclass 
do 
post_id <- param "id" 
post <- lift $ findPost post_id 
return (toJSON post) 
Haskell 
(lift . (fmap toJSON) . findPost) =<< param "id" 
JavaScript 
chain(compose(lift, map(toJSON) findPost), param(‘id'))
Typeclass 
do 
post_id <- param "id" 
post <- lift $ findPost post_id 
return (toJSON post) 
Haskell 
(lift . (fmap toJSON) . findPost) =<< param "id" 
JavaScript 
chain(compose(WebApp.lift, map(toJSON) findPost), param(‘id'))
Typeclass 
//+ savePhotos :: [Path] -> Future([Photo]) 
var savePhotos = compose(traverse(uploadPhoto), map(toBlob)) 
//+ gift :: Future(User) -> Maybe(Future(Card)) 
var gift = compose(traverse(sendCard, Maybe.of), map(_.get("birthday"))
Typeclass 
//+ savePhotos :: [Path] -> Future([Photo]) 
var savePhotos = compose(traverse(uploadPhoto), map(toBlob)) 
//+ gift :: Future(User) -> Maybe(Future(Card)) 
var gift = compose(traverse(sendCard, Maybe.of), map(_.get("birthday")) 
BAD
Typeclass 
• Universal interface (across languages) 
• Generic programs/libraries 
• Safer programs 
• Theory backed 
• Intuition
Typeclass 
• Missing polymorphic “point” 
• Working ‘blind’ with stacked containers 
• No syntactic sugar
Demos
Shortcut Fusion 
// g :: forall b. (t -> b -> b) -> b -> b 
reduce(c, n, build(g)) = g(c, n)
Shortcut Fusion 
//build :: (forall b. (a -> b -> b) -> b -> b) -> [a] 
var build = function(g){ 
return g(concat, []); 
} 
//+ map :: (a -> b) -> [a] -> [b] 
var map = curry(function(f, xs){ 
return build(function(c, n){ 
return reduce(function(acc, x){ return c(f(x), acc); }, n, xs); 
}); 
});
Shortcut Fusion 
var sum = reduce(add, 0); 
var sqr = function(x) {return x * x } 
var sumSqs = compose(sum, map(sqr)) 
// reduce(function(acc, x){ return add(sqr(x), acc) }, 0);
Compile while you 
compose 
//+ doorman :: [User] -> User 
var doorman = compose(first, filter(gte(21)), map(_.get('age')));
var addTwenty = memoize(function(x) { 
return x + 20; 
}) 
Memoization
Memoization 
var addTwenty = memoize(function(x) { 
return x + 20; 
}) 
addTwenty(10) // 30 
addTwenty(10) // 30 (didn't run) 
addTwenty(11) // 31
Memoization 
var getPosts = memoize(function(id) { 
return new Future(function(rej, res) { 
$.getJSON('/posts/'+id, res); 
}); 
});
Memoization 
var getPosts = memoize(function(id) { 
return new Future(function(rej, res) { 
$.getJSON('/posts/'+id, res); 
}); 
}); 
getPosts(2) // Future 
getPosts(2) // Future (didn't run) 
getPosts(3) // Future
Memoization 
var pQuery = $.toIO() 
pQuery(".troll") // IO(function(){ return $(".troll") }) 
pQuery.runIO() // [Dom, Dom] 
pQuery.runIO() // [Dom, Dom, Dom]
Parallel code 
foldMap(Sum, [2, 3, 5])
Parallel code 
liftA3(fn, A1, A2, A3)
Parallel code 
var longCalc // Int -> Future(Int) 
var collectResults = curry(function(rslt1, rslt2, rslt3){}) 
liftA3(collectResults, longCalc(20), longCalc(30), longCalc(10))
Parallel code 
var hasName // Attrs -> Validation 
liftA3(save, hasName, hasEmail, hasPhone)
THANKS! 
@drboolean

Fact, Fiction, and FP

  • 2.
    You will see • Lots of functional JavaScript code • Benefits and drawbacks • Functional Optimizations • Lots of unexplained type signatures
  • 3.
    You won’t see • Detailed explanations of the code • Definitions for functional constructs • Extensive performance analysis
  • 5.
    59 // src:: FlickrItem -> URL 60 var src = compose(_.get('m'), _.get('media')); 61 62 // srcs :: FlickrSearch -> [URL] 63 var srcs = compose(map(src), _.get('items')); 64 65 // images :: FlickrSearch -> [DOM] 66 var images = compose(map(imageTag), srcs); 67 68 // tags :: FlickrSearch -> [DOM] 69 var tags = compose(toP, _.countBy(_.id), _.filter(_.isEmpty), chain(split(‘ ‘))); 70 71 // imagesAndTags :: Tuple [DOM] [DOM] 72 var imagesAndTags = liftA2(Tuple, images, tags); 73 74 // widget :: String -> PictureBox 75 var widget = compose(PictureBox, map(imagesAndTags), getJSON, url); 76 77 /////////////////////////////////////////////////////////////////////////////////// 78 79 mconcat([widget('cats'), widget('dogs')]).fork(log, function(x){ 80 compose(setHtml($('#flickr')), _.first)(x) 81 compose(setHtml($(‘#tag_cloud')), _.last)(x) 82 }); 83 });
  • 7.
    style provides benefits.You should do it whenever it is convenient, and
  • 8.
    Questions • Whymight one do this? • What problems are there? • Is it performant? • Is it “production ready?”
  • 9.
  • 10.
    We can curry var replace = curry(function(regex, x, replaceable) { return replaceable.replace(regex, x); }); var squish = replace(/s+/g, ''); squish("I like to move it move it"); // Iliketomoveitmoveit
  • 11.
    We can compose var wackyText = compose(capitalize, reverse, squish) wackyText(“turtle power") // Rewopeltrut
  • 12.
    Pointfree var clientApp= compose(render, doThings, httpGet(‘/posts')) var serverApp = compose(sendJSON, doThings, Db.all(‘posts’)) var shellApp = compose(display, doThings, prompt("what's up?"))
  • 16.
  • 17.
    Pointfree httpGet('/post/2', function(json,err){ renderPost(json, err); });
  • 18.
  • 19.
  • 21.
    Pointfree var goodArticles= function(articles) { return _.filter(articles, function(article){ return _.isDefined(article); }) }
  • 22.
    Pointfree var goodArticles= function(articles) { return _.filter(articles, function(article){ return _.isDefined(article); }) } var goodArticles = filter(isDefined)
  • 23.
    Pointfree var goodArticles= function(articles) { return _.filter(articles, function(article){ return _.isDefined(article); }) } var goodArticles = filter(isDefined)
  • 24.
    Pointfree var goodArticles= function(articles) { return _.filter(articles, function(article){ return _.isDefined(article); }) } var goodArticles = filter(isDefined) GOOD
  • 25.
    Pointfree var getChildren= function(el) { return el.childNodes; } var getAllChildren = function(els) { return _.map(els, function(el) { return getChildren(el); }); }
  • 26.
    Pointfree var getChildren= function(el) { return el.childNodes; } var getAllChildren = function(els) { return _.map(els, function(el) { return getChildren(el); }); } var getChildren = get('childNodes') var getAllChildren = map(getChildren)
  • 27.
    Pointfree var getChildren= function(el) { return el.childNodes; } var getAllChildren = function(els) { return _.map(els, function(el) { return getChildren(el); }); } var getChildren = get('childNodes') var getAllChildren = map(getChildren)
  • 28.
    Pointfree var getChildren= function(el) { return el.childNodes; } var getAllChildren = function(els) { return _.map(els, function(el) { return getChildren(el); }); } var getChildren = get('childNodes') var getAllChildren = map(getChildren) GOOD
  • 30.
    Pointfree var grandChildren= function(selector) { var el = document.querySelector(selector); var children = getChildren(el); return map(getChildren, children); } var grandChildren = compose( map(getChildren) , getChildren , document.querySelector )
  • 31.
    Pointfree var grandChildren= function(selector) { var el = document.querySelector(selector); var children = getChildren(el); return map(getChildren, children); } var grandChildren = compose( map(getChildren) , getChildren , document.querySelector ) GOOD
  • 33.
    Pointfree var video= { src: 'http://youtube.com?v=23423' , description: 'family matters ep1' , screenshots: [ { url: 'i.ytimg.com/OrIxGlo.webp', size: ‘120x120' } , { url: 'i.ytimg.com/3rAxRdb.webp', size: ‘1020x764' } ] } var thumb = compose(_.first, _.get(‘screenshots')) var thumbUrl = compose(_.get('url'), thumb) var thumbWithHost = compose(concat('http://youtube.com'), thumbUrl) thumbWithHost(video) // http://youtube.com/i.ytimg.com/OrIxaBJ9Glo.webp
  • 34.
    Pointfree var video= { src: 'http://youtube.com?v=23423' , description: 'family matters ep1' , screenshots: [ { url: 'i.ytimg.com/OrIxGlo.webp', size: ‘120x120' } , { url: 'i.ytimg.com/3rAxRdb.webp', size: ‘1020x764' } ] } var thumbUrl = compose(_.get('url'), thumb) var thumb = compose(_.first, _.get(‘screenshots')) var thumbWithHost = compose(concat('http://youtube.com'), thumbUrl) thumbWithHost(video) // TypeError
  • 35.
    Pointfree var video= { src: 'http://youtube.com?v=23423' , description: 'family matters ep1' , screenshots: [ { url: 'i.ytimg.com/OrIxGlo.webp', size: ‘120x120' } , { url: 'i.ytimg.com/3rAxRdb.webp', size: ‘1020x764' } ] } var thumbUrl = compose(_.get('url'), thumb) var thumb = compose(_.first, _.get(‘screenshots')) var thumbWithHost = compose(concat('http://youtube.com'), thumbUrl) thumbWithHost(video) // TypeError Meh
  • 39.
    First Class? ["10","50", "20"].map(function(x){ return parseInt(x); }); // [10, 50, 20] ["10", "50", "20"].map(parseInt); // [10, NaN, NaN]
  • 40.
    First Class? ["10","50", "20"].map(function(x){ return parseInt(x); }); // [10, 50, 20] ["10", "50", "20"].map(parseInt); // [10, NaN, NaN] // "The Madness of King JavaScript" by Reg Braithwaite (raganwald) BAD
  • 41.
    First Class? varphone = { dial: function(x){ console.log("DIALING: ", this.format(x)); }, format: function(n) { return n.replace(/-/g, '') } }; var numbers = ["234-3535-2342", “1-653-124-8321"]; numbers.map(function(x){ return phone.dial(x); }); // DIALING: 23435352342 // DIALING: 16531248321
  • 42.
    First Class? varphone = { dial: function(x){ console.log("DIALING: ", this.format(x)); }, format: function(n) { return n.replace(/-/g, '') } }; var numbers = ["234-3535-2342", “1-653-124-8321"]; numbers.map(function(x){ return phone.dial(x); }); // DIALING: 23435352342 // DIALING: 16531248321 numbers.map(phone.dial);
  • 43.
    First Class? varphone = { dial: function(x){ console.log("DIALING: ", this.format(x)); }, format: function(n) { return n.replace(/-/g, '') } }; var numbers = ["234-3535-2342", “1-653-124-8321"]; numbers.map(function(x){ return phone.dial(x); }); // DIALING: 23435352342 // DIALING: 16531248321 numbers.map(phone.dial); // TypeError: Object #<Object> has no method 'format'
  • 44.
    First Class? varphone = { dial: function(x){ console.log("DIALING: ", this.format(x)); }, format: function(n) { return n.replace(/-/g, '') } }; var numbers = ["234-3535-2342", “1-653-124-8321"]; numbers.map(function(x){ return phone.dial(x); }); // DIALING: 23435352342 // DIALING: 16531248321 numbers.map(phone.dial.bind(phone)); // DIALING: 23435352342 // DIALING: 16531248321
  • 45.
    First Class? varphone = { dial: function(x){ console.log("DIALING: ", this.format(x)); }, format: function(n) { return n.replace(/-/g, '') } }; var numbers = ["234-3535-2342", “1-653-124-8321"]; numbers.map(function(x){ return phone.dial(x); }); // DIALING: 23435352342 // DIALING: 16531248321 numbers.map(phone.dial.bind(phone)); // DIALING: 23435352342 // DIALING: 16531248321 BAD
  • 47.
    //+ render ::[Tag] -> DOM var render = compose($(‘#tag-cloud').html, tagView, addFontSizes) //+ tagCloud :: Params -> Future(DOM) var tagCloud = compose(map(render), httpGet('/tags'))
  • 48.
    //+ setHtml ::String -> Html -> IO(DOM) var setHtml = curry(function(selector, h) { return IO(function() { return $(selector).html(h); }); }); //+ render :: [Tag] -> IO(DOM) var render = compose(setHtml(‘#tag-cloud'), tagView, addFontSizes) //+ tagCloud :: Params -> Future(IO(DOM)) var tagCloud = compose(map(render), httpGet('/tags'))
  • 51.
    Pointfree • Removesunnecessary code • High level declarative apps • Encourages generic code • Encourages purity
  • 52.
    Pointfree • Orderof definition matters • Weird interop with imperative community (impure, swiss army fn’s, & ‘this’)
  • 58.
  • 59.
  • 60.
    Typeclass promise.then(function(x){ returnx + 1 }); // Promise(2) [1].map(function(x) { return x + 1 }); // [2] event_stream.subscribe(function(x) { return x + 1 }) // EventStream(2)
  • 62.
    Typeclass var Container= function(val) { this.val = val; } Container.prototype.of = function(x) { return new Container(x); } Container.prototype.chain = function(f) { return f(this.val); };
  • 63.
    Typeclass We canmap over them (a -> b) -> M(a) -> M(b)
  • 64.
    Typeclass We canmap over them (a -> b) -> M(a) -> M(b) promise.map(function(x){ return x + 1 }); // Promise(2) [1].map(function(x) { return x + 1 }); // [2] event_stream.map(function(x) { return x + 1 }) // EventStream(2) Just(1).map(function(x) { return x + 1 }) // Just(2) Nothing.map(function(x) { return x + 1 }) // Nothing IO(1).map(function(x) { return x + 1 }) // IO(2)
  • 65.
    Typeclass We canflatten/un-nest them M(M(a)) -> M(a)
  • 66.
    Typeclass We canflatten/un-nest them M(M(a)) -> M(a) [['hello']] -> ['hello'] Just(Just(true)) -> Just(true)
  • 67.
    Typeclass We canapply functions within them M(a -> b) -> M(a) -> M(b)
  • 68.
    Typeclass We canapply functions within them M(a -> b) -> M(a) -> M(b) var finished = curry(function(click, anim) { alert(“Finished!"); }); EventStream.of(finished).ap(button_clicks).ap(animation_done);
  • 69.
    Typeclass We cancombine them M(a) -> M(a) -> M(a)
  • 70.
    Typeclass We cancombine them M(a) -> M(a) -> M(a) Api.get(‘/unstarred’).concat(Api.get('/starred')) // Future([Item])
  • 71.
    Typeclass We cancompose them M(N(a)) -> MN(a)
  • 72.
    Typeclass We cancompose them M(N(a)) -> MN(a) //+ askQuestion :: IO(Maybe(String)) compose(map(map(storeResponse)), askQuestion) //+ askQuestion :: Compose(IO(Maybe(String))) compose(map(storeResponse), Compose, askQuestion)
  • 73.
    Typeclass We cancommute them M(N(a)) -> N(M(a))
  • 74.
    Typeclass We cancommute them M(N(a)) -> N(M(a)) compose(sequenceA, map(readFile)) // Future([String])
  • 75.
    Typeclass We canderive lots of this! derive(MyType, [Functor, Applicative, Foldable]) *https://github.com/fantasyland/fantasy-land/pull/66
  • 78.
    Typeclass //+ validate:: UserAttrs -> Either([Error], UserAttrs) //+ saveUser :: UserAttrs -> Future(User) //+ emailPassword :: User -> Future(Email) //+ saveThenEmail :: UserAttrs -> Future(Email) var saveThenEmail = compose(chain(emailPassword), saveUser) //+ createUser :: UserAttrs -> Either([Error], Future(Email)) var createUser = compose(map(saveThenEmail), validate)
  • 79.
    Typeclass //+ validate:: UserAttrs -> Either([Error], UserAttrs) //+ saveUser :: UserAttrs -> Future(User) //+ emailPassword :: User -> Future(Email) //+ saveThenEmail :: UserAttrs -> Future(Email) var saveThenEmail = compose(chain(emailPassword), saveUser) //+ createUser :: UserAttrs -> Either([Error], Future(Email)) var createUser = compose(map(saveThenEmail), validate) GOOD
  • 83.
  • 85.
    Typeclass //+ lookup:: String -> {String: a} -> a|null var lookup = curry(function(x, obj) { return obj[x]; }) var upperName = compose(toUpperCase, lookup(‘name')) upperName({name: “Tori Amos"}) // “TORI AMOS” upperName({first_name: "Tori", last_name: “Spelling"}) // BOOM!
  • 86.
    Typeclass //+ safeLookup:: String -> {String: a} -> Maybe(a) var safeLookup = curry(function(x, obj) { return Maybe.fromNullable(obj[x]); }); var upperName = compose(map(toUpperCase), safeLookup(‘name')) upperName({name: “Tori Amos”}) // Just(“TORI AMOS”) upperName({first_name: "Tori", last_name: “Spelling”}) // Nothing
  • 87.
    Typeclass //+ safeLookup:: String -> {String: a} -> Maybe(a) var safeLookup = curry(function(x, obj) { return Maybe.fromNullable(obj[x]); }); var upperName = compose(map(toUpperCase), safeLookup(‘name')) upperName({name: “Tori Amos”}) // Just(“TORI AMOS”) upperName({first_name: "Tori", last_name: “Spelling”}) // Nothing GOOD
  • 89.
  • 91.
    Typeclass //+ readFile:: Future(Maybe(String)) //+ lipogram :: String -> Future(Maybe(String)) var lipogram = compose(map(map(replace(/e/ig))), readFile);
  • 92.
    Typeclass //+ readFile:: String -> Compose(Future(Maybe(String))) var readFile = compose(Compose, _readFile) //+ lipogram :: String -> Compose(Future(Maybe(String))) var lipogram = compose(map(replace(/e/ig)), readFile);
  • 93.
    Typeclass var WebApp= ReaderT(StateT(Future))
  • 94.
    Typeclass Meh varWebApp = ReaderT(StateT(Future))
  • 96.
    Typeclass do a<- Just 3 b <- Just 1 return a + b Just(3).chain(function(a) { return Just(1).chain(function(b) { return Maybe.of(a + b); }); }; Haskell JavaScript
  • 97.
    Typeclass do a<- Just 3 b <- Just 1 return a + b Haskell JavaScript liftM2(add, Just(3), Just(1))
  • 98.
    Typeclass do post_id<- param "id" post <- lift $ findPost post_id return (toJSON post) Haskell (lift . (fmap toJSON) . findPost) =<< param "id" JavaScript chain(compose(lift, map(toJSON), findPost), param(‘id'))
  • 99.
    Typeclass do post_id<- param "id" post <- lift $ findPost post_id return (toJSON post) Haskell (lift . (fmap toJSON) . findPost) =<< param "id" JavaScript chain(compose(lift, map(toJSON), findPost), param(‘id')) Meh
  • 101.
    Typeclass do post_id<- param "id" post <- lift $ findPost post_id return (toJSON post) Haskell (lift . (fmap toJSON) . findPost) =<< param "id" JavaScript chain(compose(lift, map(toJSON) findPost), param(‘id'))
  • 102.
    Typeclass do post_id<- param "id" post <- lift $ findPost post_id return (toJSON post) Haskell (lift . (fmap toJSON) . findPost) =<< param "id" JavaScript chain(compose(WebApp.lift, map(toJSON) findPost), param(‘id'))
  • 103.
    Typeclass //+ savePhotos:: [Path] -> Future([Photo]) var savePhotos = compose(traverse(uploadPhoto), map(toBlob)) //+ gift :: Future(User) -> Maybe(Future(Card)) var gift = compose(traverse(sendCard, Maybe.of), map(_.get("birthday"))
  • 104.
    Typeclass //+ savePhotos:: [Path] -> Future([Photo]) var savePhotos = compose(traverse(uploadPhoto), map(toBlob)) //+ gift :: Future(User) -> Maybe(Future(Card)) var gift = compose(traverse(sendCard, Maybe.of), map(_.get("birthday")) BAD
  • 105.
    Typeclass • Universalinterface (across languages) • Generic programs/libraries • Safer programs • Theory backed • Intuition
  • 106.
    Typeclass • Missingpolymorphic “point” • Working ‘blind’ with stacked containers • No syntactic sugar
  • 112.
  • 115.
    Shortcut Fusion //g :: forall b. (t -> b -> b) -> b -> b reduce(c, n, build(g)) = g(c, n)
  • 116.
    Shortcut Fusion //build:: (forall b. (a -> b -> b) -> b -> b) -> [a] var build = function(g){ return g(concat, []); } //+ map :: (a -> b) -> [a] -> [b] var map = curry(function(f, xs){ return build(function(c, n){ return reduce(function(acc, x){ return c(f(x), acc); }, n, xs); }); });
  • 117.
    Shortcut Fusion varsum = reduce(add, 0); var sqr = function(x) {return x * x } var sumSqs = compose(sum, map(sqr)) // reduce(function(acc, x){ return add(sqr(x), acc) }, 0);
  • 118.
    Compile while you compose //+ doorman :: [User] -> User var doorman = compose(first, filter(gte(21)), map(_.get('age')));
  • 120.
    var addTwenty =memoize(function(x) { return x + 20; }) Memoization
  • 121.
    Memoization var addTwenty= memoize(function(x) { return x + 20; }) addTwenty(10) // 30 addTwenty(10) // 30 (didn't run) addTwenty(11) // 31
  • 122.
    Memoization var getPosts= memoize(function(id) { return new Future(function(rej, res) { $.getJSON('/posts/'+id, res); }); });
  • 123.
    Memoization var getPosts= memoize(function(id) { return new Future(function(rej, res) { $.getJSON('/posts/'+id, res); }); }); getPosts(2) // Future getPosts(2) // Future (didn't run) getPosts(3) // Future
  • 124.
    Memoization var pQuery= $.toIO() pQuery(".troll") // IO(function(){ return $(".troll") }) pQuery.runIO() // [Dom, Dom] pQuery.runIO() // [Dom, Dom, Dom]
  • 125.
  • 126.
  • 127.
    Parallel code varlongCalc // Int -> Future(Int) var collectResults = curry(function(rslt1, rslt2, rslt3){}) liftA3(collectResults, longCalc(20), longCalc(30), longCalc(10))
  • 128.
    Parallel code varhasName // Attrs -> Validation liftA3(save, hasName, hasEmail, hasPhone)
  • 132.