Other Functors: Exercises 1-5
For all exercises, we define a log
function:
function log (x) { console.log(x); return x }
// Exercise 1
// ==========
// Use getPost(id) to return a Future of the title of the post ({id: i, title: 'Love them futures'})
console.log("--------Start exercise 1--------")
var ex1 = compose(map(_.get('title')), getPost)
ex1(3).fork(log, function(title){
assertEqual('Love them futures', title)
console.log("exercise 1..ok!")
})
This example will run async, and will log
out later. This pattern is great for API stuff that happen async.
// Exercise 2
// ==========
// Use ex1 to extend the computation and render the title in a div
console.log("--------Start exercise 2--------")
var render = function(x){ return "<div>"+x+"</div>"; }
// we're extendig ex1 !!!
var ex2 = compose(map(render), ex1)
ex2(3).fork(log, function(html){
assertEqual('<div>Love them futures</div>', html)
console.log("exercise 2...ok!")
})
Same pattern as before...
The point here is that in end we still call .fork
on it. But we can extend the functionalities before, like above adding another processing. (We're extending what we did in ex1
).
We are making these lazy computations, that can be extended.
Get a stream of click events from a button and use it.
// Exercise 3
// ==========
// In JSBin, click the "Output" tab to see a div. Click this div to run the test.
// Turn the clicks into a stream of the div's innerHTML
console.log("--------Start exercise 3--------")
var clicks = Bacon.fromEventTarget(document.querySelector("#box"), "click")
//Todo: turn clicks into a stream of the e.target.innerHTML
var htmlClicks = clicks.map(function(e){ return e.target.innerHTML; })
htmlClicks.onValue(function(html){
assertEqual('<span>CLICK ME</span>', trim(html))
console.log("exercise 3...ok!")
})
// Exercise 4
// ==========
// Keep the Output tab open. Type into the input to run the test.
// Transform the keydowns into a stream of the input's value
// Then use pureLog() to log it to the console
console.log("--------Start exercise 4--------")
var pureLog = function(x){ console.log(x); return x; }.toIO();
var search_input = document.querySelector("#search")
var keydowns = Bacon.fromEventTarget(search_input, "keydown")
//Todo: turn keydowns into a stream of the logged input's value
var logs = keydowns;
logs.onValue(function(io){
assertEqual(search_input.value, runIO(pureLog))
console.log("exercise 4...ok!")
})
skips exercise 4...
// Exercise 5*
// ==========
// Use only safeGet() to safely return the street name
console.log("--------Start exercise 5--------")
var safeGet = _.curry(function(x,o){ return Maybe(o[x]) })
var user = {id: 2, name: "Albert", address: { street: {number: 22, name: 'Walnut St'} } }
var ex5 = compose(map(map(safeGet('name'))), map(safeGet('street')), safeGet('address'));
assertDeepEqual(Maybe(Maybe(Maybe('Walnut St'))), ex5(user))
console.log("exercise 5...ok!")
Exercise 5 is more difficult.
var ex5 = compose(safeGet('address'))
this above line gives me Maybe
of the address.
var ex5 = compose(map(safeGet('street')), safeGet('address'))
now (above line) we need to map
over the Maybe
that is returned by safeGet('address')
.
Now we have a Maybe(Maybe(val))
. So far in the course, we don't have the tools to flatten this nested Maybe
. So this is why in the next line we have to map(map(safeGet('name')))
.
var ex5 = compose(map(map(safeGet('name'))), map(safeGet('street')), safeGet('address'))
We have 3 maybes wrapped. We have 3 null checks going on. This is a big nesting of null checks. It's like a big tree of nested null checking. That is a monad.