Skip to content

Commit

Permalink
Update ch5.md
Browse files Browse the repository at this point in the history
Normalize semicolons and variable assignments to avoid linting errors
  • Loading branch information
mdavidgreen committed May 17, 2015
1 parent b45f928 commit c4003d1
Showing 1 changed file with 55 additions and 53 deletions.
108 changes: 55 additions & 53 deletions ch5.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ Here's `compose`:
var compose = function(f,g) {
return function(x) {
return f(g(x));
}
}
};
};
```

`f` and `g` are functions and `x` is the value being "piped" through them.

Composition feels like function husbandry. You, breeder of functions, select two with traits you'd like to combine and mash them together to spawn a brand new one. Usage is as follows:

```js
var toUpperCase = function(x) { return x.toUpperCase() }
var exclaim = function(x) { return x + '!' }
var shout = compose(exclaim, toUpperCase)
var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);

shout("send in the clowns")
shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"
```

Expand All @@ -31,50 +31,51 @@ In our definition of `compose`, the `g` will run before the `f`, creating a righ

```js
var shout = function(x){
return exclaim(toUpperCase(x))
}
return exclaim(toUpperCase(x));
};
```

Instead of inside to outside, we run right to left, which I suppose is a step in the left direction[^boo]. Let's look at an example where sequence matters:

```js
var head = function(x) { return x[0]; }
var head = function(x) { return x[0]; };
var reverse = reduce(function(acc, x){ return [x].concat(acc); }, []);
var last = compose(head, reverse)
var last = compose(head, reverse);

last(['jumpkick', 'roundhouse', 'uppercut'])
last(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'uppercut'
```

`reverse` will turn the list around while `head` grabs the initial item. This results in an effective, albeit inefficient, `last` function. The sequence of functions in the composition should be apparent here. We could define a left to right version, however, we mirror the mathematical version much more closely as it stands. That's right, composition is straight from the math books. In fact, perhaps it's time to look at a property that holds for any composition.

```js
// associativity
compose(f, compose(g, h)) == compose(compose(f, g), h)
var associative = compose(f, compose(g, h) == compose(compose(f, g), h));
// true
```

Composition is associative, meaning it doesn't matter how you group two of them. So, should we choose to uppercase the string, we can write:

```js
compose(toUpperCase, compose(head, reverse))
compose(toUpperCase, compose(head, reverse));

// or
compose(compose(toUpperCase, head), reverse)
compose(compose(toUpperCase, head), reverse);
```

Since it doesn't matter how we group our calls to `compose`, the result will be the same. That allows use to write a variadic compose and use it as follows:

```js
// previously we'd have to write two composes, but since it's associative, we can give compose as many fn's as we like and let it decide how to group them.
var lastUpper = compose(toUpperCase, head, reverse)
var lastUpper = compose(toUpperCase, head, reverse);

lastUpper(['jumpkick', 'roundhouse', 'uppercut'])
lastUpper(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'UPPERCUT'


var loudLastUpper = compose(exclaim, toUpperCase, head, reverse)

loudLastUpper(['jumpkick', 'roundhouse', 'uppercut'])
loudLastUpper(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'UPPERCUT!'
```

Expand All @@ -83,16 +84,16 @@ Applying the associative property gives us this flexibility and peace of mind th
One pleasant benefit of associativity is that any group of functions can be extracted and bundled together in their very own composition. Let's play with refactoring our previous example:

```js
var loudLastUpper = compose(exclaim, toUpperCase, head, reverse)
var loudLastUpper = compose(exclaim, toUpperCase, head, reverse);

// or
var last = compose(head, reverse)
var loudLastUpper = compose(exclaim, toUpperCase, last)
var last = compose(head, reverse);
var loudLastUpper = compose(exclaim, toUpperCase, last);

// or
var last = compose(head, reverse)
var angry = compose(exclaim, toUpperCase)
var loudLastUpper = compose(angry, last)
var last = compose(head, reverse);
var angry = compose(exclaim, toUpperCase);
var loudLastUpper = compose(angry, last);

// more variations...
```
Expand All @@ -107,10 +108,10 @@ Pointfree style means never having to say your data. Excuse me. It means functio
//not pointfree because we mention the data: word
var snakeCase = function (word) {
return word.toLowerCase().replace(/\s+/ig, '_');
}
};

//pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase)
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
```

See how we partially applied `replace`? What we're doing is piping our data through each function of 1 argument. Currying allows us to prepare each function to just take it's data, operate on it, and pass it along. Something else to notice is how we don't need the data to construct our function in the pointfree version, whereas in the pointful one, we must have our `word` available before anything else.
Expand All @@ -121,12 +122,12 @@ Let's look at another example.
//not pointfree because we mention the data: name
var initials = function (name) {
return name.split(' ').map(compose(toUpperCase, head)).join('. ');
}
};

//pointfree
var initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '))
var initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '));

initials("hunter stockton thompson")
initials("hunter stockton thompson");
// 'H. S. T'
```

Expand All @@ -137,46 +138,46 @@ A common mistake is to compose something like `map`, a function of two arguments

```js
//wrong - we end up giving angry an array and we partially applied map with god knows what.
var latin = compose(map, angry, reverse)
var latin = compose(map, angry, reverse);

latin(["frog", "eyes"])
latin(["frog", "eyes"]);
// error


// right - each function expects 1 argument.
var latin = compose(map(angry), reverse)
var latin = compose(map(angry), reverse);

latin(["frog", "eyes"])
latin(["frog", "eyes"]);
// ["SEYE!", "GORF!"])
```

If you are having trouble debugging a composition, we can use this helpful, but impure trace function to see what's going on.

```js
var trace = curry(function(tag, x){
console.log(tag, x)
return x
})
console.log(tag, x);
return x;
});

var dasherize = compose(join('-'), replace(/\s{2,}/ig, ' '), split(' '))
var dasherize = compose(join('-'), replace(/\s{2,}/ig, ' '), split(' '));

dasherize('the world is a vampire')
dasherize('the world is a vampire');
// TypeError: Object the,world,is,a,vampire has no method 'replace'
```

Something is wrong here, let's `trace`

```js
var dasherize = compose(join('-'), replace(/\s{2,}/ig, ' '), trace("after split"), split(' '))
var dasherize = compose(join('-'), replace(/\s{2,}/ig, ' '), trace("after split"), split(' '));
// after split [ 'the', 'world', 'is', 'a', 'vampire' ]
```

Ah! We need to `map` this `replace` since it's working on an array.

```js
var dasherize = compose(join('-'), map(replace(/\s{2,}/ig, ' ')), split(' '))
var dasherize = compose(join('-'), map(replace(/\s{2,}/ig, ' ')), split(' '));

dasherize('the world is a vampire')
dasherize('the world is a vampire');

// 'the-world-is-a-vampire'
```
Expand Down Expand Up @@ -221,16 +222,16 @@ Here is an image demonstrating composition:
Here is a concrete example in code:

```js
var g = function(x){ return x.length }
var f = function(x){ return x === 4 }
var isFourLetterWord = compose(f, g)
var g = function(x){ return x.length; };
var f = function(x){ return x === 4; };
var isFourLetterWord = compose(f, g);
```

**A distinguished morphism called identity**
Let's introduce a useful function called `id`. This function simply takes some input and spits it back at you. Take a look:

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

You might ask yourself "What in the bloody hell is that useful for?". We'll make extensive use of this function in the following chapters, but for now think of it as a function that can stand in for our value - a function masquerading as every day data.
Expand All @@ -239,7 +240,8 @@ You might ask yourself "What in the bloody hell is that useful for?". We'll make

```js
// identity
compose(id, f) == compose(f, id) == f
var identity = compose(id, f) == compose(f, id) == f;
// true
```

Hey, it's just like the identity property on numbers! If that's not immediately clear, take some time with it. Understand the futility. We'll be seeing `id` used all over the place soon, but for now we see it's a function that acts as a stand in for a given value. This is quite useful when writing pointfree code.
Expand All @@ -261,7 +263,7 @@ We are now at a point where it would serve us well to see some of this in practi
## Exercises

```js
require('../../support')
require('../../support');
var _ = require('ramda');
var accounting = require('accounting');

Expand All @@ -279,9 +281,9 @@ var CARS = [
// ============
// use _.compose() to rewrite the function below. Hint: _.prop() is curried.
var isLastInStock = function(cars) {
var last_car = _.last(cars)
var last_car = _.last(cars);
return _.prop('in_stock', last_car);
}
};

// Exercise 2:
// ============
Expand All @@ -297,7 +299,7 @@ var _average = function(xs) { return reduce(add, 0, xs) / xs.length; }; // <- le
var averageDollarValue = function(cars) {
var dollar_values = map(function(c) { return c.dollar_value; }, cars);
return _average(dollar_values);
}
};


// Exercise 4:
Expand All @@ -306,7 +308,7 @@ var averageDollarValue = function(cars) {

var _underscore = replace(/\W+/g, '_'); //<-- leave this alone and use to sanitize

var sanitizeNames = undefined
var sanitizeNames = undefined;


// Bonus 1:
Expand All @@ -316,9 +318,9 @@ var sanitizeNames = undefined
var availablePrices = function(cars) {
var available_cars = _.filter(_.prop('in_stock'), cars);
return available_cars.map(function(x){
return accounting.formatMoney(x.dollar_value)
return accounting.formatMoney(x.dollar_value);
}).join(', ');
}
};


// Bonus 2:
Expand All @@ -329,7 +331,7 @@ var fastestCar = function(cars) {
var sorted = _.sortBy(function(car){ return car.horsepower }, cars);
var fastest = _.last(sorted);
return fastest.name + ' is the fastest';
}
};
```

[lodash-website]: https://lodash.com/
Expand Down

0 comments on commit c4003d1

Please sign in to comment.