Applicative Functor
We've arleady seen pointed functors, this is another kind.
“Run full computations in a context”
functions: ap, liftA2, liftA..n
We get a partially applied Container
↴
map(add(1), Container(2))
//=> Container(3)
map(add, Container(2))
// => Container(add(2))
This is not ideal. Let's try to solve this with our current toolset.
Code example:
var add = _curry(function (x,y) {return x + y})
var c2 = Container(2)
var c3 = Container(2)
var c_add2 = map(add, c2) // Container(add(2))
var res = map(function (add2) {
return map(add2, c3)
}, c_add2)
console.log(res) // => Container(Container(5))
Let's continue with flatmap
var add = _curry(function (x,y) {return x + y})
var c2 = Container(2)
var c3 = Container(2)
var c_add2 = map(add, c2) // Container(add(2))
var res = flatMap(function (add2) {
return map(add2, c3)
}, c_add2)
console.log(res) // => Container(5)
This is better, but still a bit awkward. Could be cleaner.
#Applicatives
ap :: A (a -> b) -> A a -> A b
Pointed Functor + ap = Applicative
aka: <*>
Reviews Pointed Functors: functors that have an of
method:
of :: a -> F a
Usage examples of pointer functions:
Container.of(split) Future.of(match(/dubstep/))
// Containe(split) // Future(match(/dubstep/))
Maybe.of(reverse) EventStream.of(replace(/dubstep/, 'shoegaze'))
// Maybe(reverse) // EventStream(match(/dubstep/, 'shoegaze'))
Explains that the of
pattern is even better with Futures, as they expect a callback function.
##Applicatives
We can apply a Container
of a function on a Container
of a value:
Container.of(f).ap(Container(x))
//=> Container(f(x))
// It can even apply the container of a function on both containers:
Container.of(f).ap(Container(x)).ap(Container(y))
//=> Container(f(x, y))
// This chains the functors
A more concrete example:
Container.of(add).ap(Container(1)).ap(Container(3))
//=> Container(4)
add
gets partially applied to each of the calls (so add
has to be curried).
###Applicatives
Same as before but with Maybe
.
Maybe.of(add).ap(Maybe(1)).ap(Maybe(3))
//=> Maybe(4)
Maybe.of(add).ap(Maybe(1)).ap(Maybe(null))
// this will not run at all
//=> Maybe(null)
###Applicatives
var loadPage = _.curry(function(products, reviews){ render(products.zip(reviews) })
Future.of(loadPage).ap(Api.get('/products')).ap(Api.get('/reviews'))
What's great is that you can wait for two independent asynchronous calls! It will wait for both them to finish (gate).
####EventStream
var showLoaded = _.curry(function(tweetbtn, fbbtn){ alert('Done!') })
EventStream.of(showLoaded).ap(tweet_btn.on('load')).ap(fb_btn.on('load'))
The two buttons have to be pushed to run showLoaded
.
You can also swith between futures and eventstreams (user interaction or socket.io for example)
Or validation. If either of them fail, it will not run:
var getVal = compose(Maybe, pluck('value'), document.querySelector)
var save = _.curry(function(email, pass){ return User(email, pass) })
Maybe.of(save).ap(getVal('#email')).ap(getVal('#password'))
//=> Maybe(user)
Same pattern as above, exce.
So far we always chained off the of
off a specific Functor.
Introduces liftA2
which is type generic:
var getVal = compose(Maybe, pluck('value'), document.querySelector)
var save = _.curry(function(email, pass){ return User(email, pass) })
liftA2(save, getVal('#email'), getVal('#password'))
//=> Maybe(user)
Important to note, that both functors have to be of the same type, but they can be swapped out to other types.
But as long as they are the same types liftA2
is generic enough to work with any type.
var loadPage = _.curry(function(ships, orders, receipts){
return render(ships, orders, receipts)
})
liftA3(loadPage, http.get('/shipments'), http.get('/orders'), http.get('/receipts'))
//=> Future(Dom)
liftA3
takes three arguments
http://jsbin.com/perutuyijo/1
answer: answers: http://jsbin.com/rusacuqidu/1
###Exercises
jsbin.com/zowev/4/edit
// Exercise 1
// ==========
// Write a function that add's two possibly null numbers together using Maybe and ap()
console.log("--------Start exercise 1--------")
var ex1 = undefined;
// solution :
var ex1 = function (x,y) {
return Maybe.of(_.add).ap(Maybe(x)).ap(Maybe(y))
}
assertDeepEqual(Maybe(5), ex1(2, 3))
assertDeepEqual(Maybe(null), ex1(null, 3))
console.log("exercise 1...ok!")
// Exercise 2
// ==========
// Rewrite 1 to use liftA2 instead of ap()
console.log("--------Start exercise 2--------")
var ex2 = undefined;
// solution
var ex2 = liftA2(_.add)
assertDeepEqual(Maybe(5), ex2(Maybe(2), Maybe(3)))
assertDeepEqual(Maybe(null), ex2(Maybe(null), Maybe(3)))
console.log("exercise 2...ok!")
liftA2
is curried and can be partially applied
// Exercise 3
// ==========
// Make a future by running getPost() and getComments() using applicatives, then renders the page with both
var makeComments = _.reduce(function(acc, c){ return acc+"<li>"+c+"</li>"}, "")
var render = _.curry(function(post, comments){
return "<div>"+post.title+"</div>" + makeComments(comments);
})
console.log("--------Start exercise 3--------")
var ex3 = undefined
// solution
// var ex3 = getPost(), getComments()
// then:
var ex3 = liftA2(render, getPost(), getComments())
// getPost is down in the linked jsbin file
// the point is that it returns a future
// futures take a fork method to run
ex3.fork(log, function(html){
assertEqual("<div>Love them futures</div><li>This class should be illegal</li><li>Monads are like space burritos</li>", html)
console.log("exercise 3...ok!")
})
// Exercise 4
// ==========
// setup...
localStorage.player1 = "toby"
localStorage.player2 = "sally"
// Write a function that gets both player1 and player2 from the cache.
var getCache = function(x){ return localStorage[x]; }.toIO();
var game = _.curry(function(p1, p2){ return p1 + ' vs ' + p2 })
console.log("--------Start exercise 4--------")
var ex4 = undefined;
// solution
var ex4 = liftA2(game, getCache('player1'), getCache('player2'))
// these returns IOs that we apply to game
assertEqual("toby vs sally", runIO(ex4))
console.log("exercise 4...ok!")
###Applicative laws
// identity
A(id).ap(m) == m
// composition
A(compose).ap(f).ap(g).ap(w) == f.ap(g.ap(w)))
// homomorphism
A(f).ap(A(x)) == A(f(x))
// interchange
u.ap(A(y)) == A(function(f) { return f(y) }).ap(u)
#Monoids
Combination/Accumulation
In this presentation we will focus on combination and accumulation.
functions: empty, concat, mconcat
// Monoids
reduce(function (acc, x) {
return acc + x
}, 0, [1,2,3])
// Monoids
reduce(function (acc, x) {
return acc * x
}, 0, [1,2,3])
// Monoids
reduce(function (acc, x) {
return acc || x
}, false, [false, false, true])
// Monoids
reduce(function (acc, x) {
return acc && x
}, true, [false, false, true])
Monoids are built on semigroups.
####Semigroup
anything that has a
concat
(combination) method
it can be plus, multiplication, etc.(see above examples)
####Monoid
Any semigroup that has an
empty()
method
The empty()
returns a neutral element. It's the second argument to reduce.
###Monoid
_Sum = function (v) {
this.val = v
}
// so far this like a functor (container)
// but the val has to be a number
_Sum.prototype.concat = function (s2) {
return Sum(this.val + s2.val)
}
_Sum.prototype.empty = function () { return Sum(0) }
There are a lot of different monoids... In your work it's very likely that you are going to be making these monoids.
Sum(2).concat(Sum(3))
// => Sum(5)
Sum(2).concat(Sum(3)).concat(Sum(5))
// => Sum(10)
mconcat([Sum(2), Sum(3), Sum(5)])
// => Sum(10)
mconcat
is doing a reduce function for us, and it knows the type so it knows that it has to add two numbers together.
mconcat([Product(2), Product(3), Product(5)])
// => Sum(10)
So mconcat
is generic enough to work with any type.
mconcat([Max(2), Max(3), Max(5)])
// => Max(5)
Above, he demonstrates that these can be combined with any types.
compose(getResult, mconcat, map(Sum))([1,2,3])
// => 6
compose(toUpperCase, reverse])(['bonkers'])
// => 'BONKERSsreknob'
functions are monoids as well. (from the above example)
###Exercises
jsbin.com/jeqof
// Exercise 1
// ==========
// rewrite the ex1 function to use getResult() mconcat() and Sum() instead of sum()
console.log("--------Start exercise 1--------")
var sum = _.reduce(_.add, 0)
var ex1 = sum // <----- rewrite me
var ex1 = compose(getResult, mconcat, map(Sum))
assertEqual(6, ex1([1,2,3]))
console.log("exercise 1...ok!")
// Exercise 2
// ==========
// Similar to the above, get the Product of the list.
console.log("--------Start exercise 2--------")
ex2 = undefined
var ex2 = compose(getResult, mconcat, map(Product))
assertEqual(12, ex2([2,2,3]))
console.log("exercise 2...ok!")
// Exercise 3
// ==========
// Similar to the above, get the Max of the list.
console.log("--------Start exercise 3--------")
ex3 = undefined
var ex3 = compose(getResult, mconcat, map(Max))
assertEqual(32, ex3([12,32,3]))
console.log("exercise 3...ok!")
// Exercise 4
// ==========
// use the function monoid instance to mconcat the functions below to create a full name string.
console.log("--------Start exercise 4--------")
var firstName = _.get('first')
var middleName = _.get('middle')
var lastName = _.get('last')
var space = _.K(' ')
var ex4 = undefined
var ex4 = mconcat([firstName, space, middleName, lastName])
var user = {first: "Bill", middle: "Jefferson", last: "Clinton"}
assertEqual("Bill Jefferson Clinton", ex4(user))
console.log("exercise 4...ok!")
// Exercise 5
// ==========
// For Tuple to be a monoid, it's x,y must also be monoids. Monoids beget monoids.
// Use this information to complete the definition of Tuple's concat fn.
console.log("--------Start exercise 5--------")
var Tuple = _.curry(function(x, y) { return new _Tuple(x, y) })
var _Tuple = function(x, y) {
this.x = x;
this.y = y;
}
_Tuple.prototype.inspect = function() {
return 'Tuple('+inspect(this.x)+' '+inspect(this.y)+')';
}
_Tuple.prototype.empty = function() { return Tuple(this.x.empty(), this.y.empty()) };
// TODO: DEFINE ME
_Tuple.prototype.concat = function(t2) {
// solution
return Tuple(this.x.concat(t2.x), this.y.concat(t2.y))
};
var ex5 = mconcat([Tuple("abc", [1,2,3]), Tuple("def", [4,5,6])])
assertDeepEqual(Tuple("abcdef", [1,2,3,4,5,6]), ex5)
console.log("exercise 5...ok!")
So here we could combine two values into one (Tuple)
###Monoids
mconcat([Failure(['message1']), Success(attrs), Failure(['message2'])])
// => Failure(['message1', 'message2'])
We subclass validation into failure and success.
var checkValidations = mconcat([checkPassword, checkEmail, checkName])
checkValidations({name:'Burt'})
// => Failure(['need a password', 'need an emai'])
We combine several validations into one.
###Monoid laws
// left identity
concat(empty, x) == x
// right identity
concat(x, empty) == x
// associativity
concat(concat(x, y), z) == concat(x, concat(y, z))
Explains that these above laws are exactly the same as the monad and category laws. (see below)
###Category laws
// left identity
compose(id, f) == f
// right identity
compose(f, id) == f
// associativity
compose(compose(f, g), h) == compose(f, compose(g, h))
###Monad laws
// left identity
mcompose(M, f) == f
// right identity
mcompose(f, M) == f
// associativity
mcompose(mcompose(f, g), h) == mcompose(f, mcompose(g, h))
##Fantasy Land
Fantasy land specs define the names that you should be using. Here we did not always follow these specs. The importance of fantasyland is that your code will be compatible with others so you can compose your code better.