Skip to content

Commit a73aa3b

Browse files
committed
backporting layout/formatting/wording tweaks/typo fixes, from leanpub branch
1 parent cf8251e commit a73aa3b

File tree

15 files changed

+220
-178
lines changed

15 files changed

+220
-178
lines changed

apA.md

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Functional-Light JavaScript
22
# Appendix A: Transducing
33

4-
Transducing is a more advanced technique than we've covered in this book. It extends many of the concepts from Chapter 9 on list operations.
4+
Transducing is a more advanced technique than we've covered in this book. It extends many of the concepts from [Chapter 9](ch9.md) on list operations.
55

66
I wouldn't necessarily call this topic strictly "Functional-Light", but more like a bonus on top. I've presented this as an appendix because you might very well need to skip the discussion for now and come back to it once you feel fairly comfortable with -- and make sure you've practiced! -- the main book concepts.
77

@@ -15,7 +15,7 @@ As with the rest of this book, my approach is to first explain *why*, then *how*
1515

1616
## Why, First
1717

18-
Let's start by extending a scenario we covered back in Chapter 3, testing words to see if they're short enough and/or long enough:
18+
Let's start by extending a [scenario we covered back in Chapter 3](ch3.md/#user-content-shortlongenough), testing words to see if they're short enough and/or long enough:
1919

2020
```js
2121
function isLongEnough(str) {
@@ -40,7 +40,7 @@ words
4040

4141
It may not be obvious, but this pattern of separate adjacent list operations has some non-ideal characteristics. When we're dealing with only a single array of a small number of values, everything is fine. But if there were lots of values in the array, each `filter(..)` processing the list separately can slow down a bit more than we'd like.
4242

43-
A similar performance problem arises when our arrays are async/lazy (aka Observables), processing values over time in response to events (see Chapter 10). In this scenario, only a single value comes down the event stream at a time, so processing that discrete value with two separate `filter(..)`s function calls isn't really such a big deal.
43+
A similar performance problem arises when our arrays are async/lazy (aka Observables), processing values over time in response to events (see [Chapter 10](ch10.md)). In this scenario, only a single value comes down the event stream at a time, so processing that discrete value with two separate `filter(..)`s function calls isn't really such a big deal.
4444

4545
But what's not obvious is that each `filter(..)` method produces a separate observable. The overhead of pumping a value out of one observable into another can really add up. That's especially true since in these cases, it's not uncommon for thousands or millions of values to be processed; even such small overhead costs add up quickly.
4646

@@ -66,7 +66,7 @@ function isCorrectLength(str) {
6666

6767
But that's not the FP way!
6868

69-
In Chapter 9, we talked about fusion -- composing adjacent mapping functions. Recall:
69+
In [Chapter 9, we talked about fusion](ch9.md/#fusion) -- composing adjacent mapping functions. Recall:
7070

7171
```js
7272
words
@@ -108,7 +108,7 @@ Let's jump in.
108108

109109
### Expressing Map/Filter as Reduce
110110

111-
The first trick we need to perform is expressing our `filter(..)` and `map(..)` calls as `reduce(..)` calls. Recall how we did that in Chapter 9:
111+
The first trick we need to perform is expressing our `filter(..)` and `map(..)` calls as `reduce(..)` calls. Recall [how we did that in Chapter 9](ch9.md/#map-as-reduce):
112112

113113
```js
114114
function strUppercase(str) { return str.toUpperCase(); }
@@ -139,7 +139,9 @@ words
139139

140140
That's a decent improvement. We now have four adjacent `reduce(..)` calls instead of a mixture of three different methods all with different shapes. We still can't just `compose(..)` those four reducers, however, because they accept two arguments instead of one.
141141

142-
In Chapter 9, we sort of cheated and used `list.push(..)` to mutate as a side effect rather than creating a whole new array to concatenate onto. Let's step back and be a bit more formal for now:
142+
<a name="cheating"></a>
143+
144+
In [Chapter 9, we sort of cheated](ch9.md/#user-content-reducecheating) and used `list.push(..)` to mutate as a side effect rather than creating a whole new array to concatenate onto. Let's step back and be a bit more formal for now:
143145

144146
```js
145147
function strUppercaseReducer(list,str) {
@@ -281,12 +283,13 @@ var curriedMapReducer = curry( function mapReducer(mapperFn,combinationFn){
281283
};
282284
} );
283285

284-
var curriedFilterReducer = curry( function filterReducer(predicateFn,combinationFn){
285-
return function reducer(list,val){
286-
if (predicateFn( val )) return combinationFn( list, val );
287-
return list;
288-
};
289-
} );
286+
var curriedFilterReducer = curry(
287+
function filterReducer(predicateFn,combinationFn){
288+
return function reducer(list,val){
289+
if (predicateFn( val )) return combinationFn( list, val );
290+
return list;
291+
};
292+
} );
290293

291294
var strToUppercaseReducer =
292295
curriedMapReducer( strUppercase )( listCombination );
@@ -464,7 +467,7 @@ words
464467
// ["WRITTEN","SOMETHING"]
465468
```
466469

467-
**Note:** We should make an observation about the `compose(..)` order in the previous two snippets, which may be confusing. Recall that in our original example chain, we `map(strUppercase)` and then `filter(isLongEnough)` and finally `filter(isShortEnough)`; those operations indeed happen in that order. But in Chapter 4, we learned that `compose(..)` typically has the effect of running its functions in reverse order of listing. So why don't we need to reverse the order *here* to get the same desired outcome? The abstraction of the `combinationFn(..)` from each reducer reverses the effective applied order of operations under the hood. So counter-intuitively, when composing a tranducer, you actually want to list them in desired order of execution!
470+
**Note:** We should make an observation about the `compose(..)` order in the previous two snippets, which may be confusing. Recall that in our original example chain, we `map(strUppercase)` and then `filter(isLongEnough)` and finally `filter(isShortEnough)`; those operations indeed happen in that order. But in [Chapter 4](ch4.md/#user-content-generalcompose), we learned that `compose(..)` typically has the effect of running its functions in reverse order of listing. So why don't we need to reverse the order *here* to get the same desired outcome? The abstraction of the `combinationFn(..)` from each reducer reverses the effective applied order of operations under the hood. So counter-intuitively, when composing a tranducer, you actually want to list them in desired order of execution!
468471

469472
#### List Combination: Pure vs. Impure
470473

@@ -491,7 +494,7 @@ Thinking about `listCombination(..)` in isolation, there's no question it's impu
491494

492495
`listCombination(..)` is not a function we interact with at all. We don't directly use it anywhere in the program; instead, we let the transducing process use it.
493496

494-
Back in Chapter 5, we asserted that our goal with reducing side effects and defining pure functions was only that we expose pure functions to the API level of functions we'll use throughout our program. We observed that under the covers, inside a pure function, it can cheat for performance sake all it wants, as long as it doesn't violate the external contract of purity.
497+
Back in [Chapter 5](ch5.md), we asserted that our goal with reducing side effects and defining pure functions was only that we expose pure functions to the API level of functions we'll use throughout our program. We observed that under the covers, inside a pure function, it can cheat for performance sake all it wants, as long as it doesn't violate the external contract of purity.
495498

496499
`listCombination(..)` is more an internal implementation detail of the transducing -- in fact, it'll often be provided by the transducing library for you! -- rather than a top-level method you'd interact with on a normal basis throughout your program.
497500

@@ -548,12 +551,13 @@ var transduceMap = curry( function mapReducer(mapperFn,combinationFn){
548551
};
549552
} );
550553

551-
var transduceFilter = curry( function filterReducer(predicateFn,combinationFn){
552-
return function reducer(list,v){
553-
if (predicateFn( v )) return combinationFn( list, v );
554-
return list;
555-
};
556-
} );
554+
var transduceFilter = curry(
555+
function filterReducer(predicateFn,combinationFn){
556+
return function reducer(list,v){
557+
if (predicateFn( v )) return combinationFn( list, v );
558+
return list;
559+
};
560+
} );
557561
```
558562

559563
Also recall that we use them like this:
@@ -597,7 +601,7 @@ Not bad, huh!? See the `listCombination(..)` and `strConcat(..)` functions used
597601

598602
### Transducers.js
599603

600-
Finally, let's illustrate our running example using the `transducers-js` library (https://github.com/cognitect-labs/transducers-js):
604+
Finally, let's illustrate our running example using the [`transducers-js` library](https://github.com/cognitect-labs/transducers-js):
601605

602606
```js
603607
var transformer = transducers.comp(
@@ -615,7 +619,7 @@ transducers.transduce( transformer, strConcat, "", words );
615619

616620
Looks almost identical to above.
617621

618-
**Note:** The preceding snippet uses `transformers.comp(..)` because the library provides it, but in this case our `compose(..)` from Chapter 4 would produce the same outcome. In other words, composition itself isn't a transducing-sensitive operation.
622+
**Note:** The preceding snippet uses `transformers.comp(..)` because the library provides it, but in this case our [`compose(..)` from Chapter 4](ch4.md/#user-content-generalcompose) would produce the same outcome. In other words, composition itself isn't a transducing-sensitive operation.
619623

620624
The composed function in this snippet is named `transformer` instead of `transducer`. That's because if we call `transformer(listCombination)` (or `transformer(strConcat)`), we won't get a straight-up transduce-reducer function as earlier.
621625

apB.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Functional-Light JavaScript
22
# Appendix B: The Humble Monad
33

4-
Let me just start off this appendix by admitting: I did not know much about what a monad was before starting to write this appendix. And it took a lot of mistakes to get something sensible. If you don't believe me, go look at the commit history of this appendix in the Git repo for this book (https://github.com/getify/Functional-Light-JS)!
4+
Let me just start off this appendix by admitting: I did not know much about what a monad was before starting to write this appendix. And it took a lot of mistakes to get something sensible. If you don't believe me, go look at the commit history of this appendix in the [Github repository for this book](https://github.com/getify/Functional-Light-JS)!
55

66
I am including the topic of monads in the book because it's part of the journey that every developer will encounter while learning FP, just as I have in this book writing.
77

@@ -29,7 +29,7 @@ I'm going to use the notion of data structures very loosely here, and assert tha
2929

3030
A monad is a data structure. It's a type. It's a set of behaviors that are specifically designed to make working with a value predictable.
3131

32-
Recall in Chapter 9 that we talked about functors: a value along with a map-like utility to perform an operation on all its constitute data members. A monad is a functor that includes some additional behavior.
32+
Recall in [Chapter 9 that we talked about functors](ch9.md/#a-word-functors): a value along with a map-like utility to perform an operation on all its constitute data members. A monad is a functor that includes some additional behavior.
3333

3434
## Loose Interface
3535

@@ -82,7 +82,7 @@ Don't worry if most of this doesn't make sense right now. We're not gonna obsess
8282

8383
All monad instances will have `map(..)`, `chain(..)` (also called `bind(..)` or `flatMap(..)`), and `ap(..)` methods. The purpose of these methods and their behavior is to provide a standardized way of multiple monad instances interacting with each other.
8484

85-
Let's look first at the monadic `map(..)` function. Like `map(..)` on an array (see Chapter 9) that calls a mapper function with its value(s) and produces a new array, a monad's `map(..)` calls a mapper function with the monad's value, and whatever is returned is wrapped in a new Just monad instance:
85+
Let's look first at the monadic `map(..)` function. Like `map(..)` on an array (see [Chapter 9](ch9.md/#map)) that calls a mapper function with its value(s) and produces a new array, a monad's `map(..)` calls a mapper function with the monad's value, and whatever is returned is wrapped in a new Just monad instance:
8686

8787
```js
8888
var A = Just( 10 );
@@ -103,7 +103,7 @@ typeof eleven; // "number"
103103

104104
`eleven` is the actual primitive number `11`, not a monad holding that value.
105105

106-
To connect this `chain(..)` method conceptually to stuff we've already learned, we'll point out that many monad implementations name this method `flatMap(..)`. Now, recall from Chapter 9 what `flatMap(..)` does (as compared to `map(..)`) with an array:
106+
To connect this `chain(..)` method conceptually to stuff we've already learned, we'll point out that many monad implementations name this method `flatMap(..)`. Now, recall from [Chapter 9 what `flatMap(..)`](ch9.md/#user-content-flatmap) does (as compared to `map(..)`) with an array:
107107

108108
```js
109109
var x = [3];
@@ -116,7 +116,7 @@ See the difference? The mapper function `v => [v,v+1]` results in a `[3,4]` arra
116116

117117
That's the same kind of thing going on with a monad's `chain(..)` (aka `flatMap(..)`). Instead of getting a monad holding the value as `map(..)` does, `chain(..)` additionally flattens the monad into the underlying value. Actually, instead of creating that intermediate monad only to immediately flatten it, `chain(..)` is generally implemented more performantly to just take a shortcut and not create the monad in the first place. Either way, the end result is the same.
118118

119-
One way to illustrate `chain(..)` in this manner is in combination with the `identity(..)` utility (see Chapter 3), to effectively extract a value from a monad:
119+
One way to illustrate `chain(..)` in this manner is in combination with the `identity(..)` utility (see [Chapter 3](ch3.md/#one-on-one)), to effectively extract a value from a monad:
120120

121121
```js
122122
var identity = v => v;
@@ -150,7 +150,7 @@ Now, how could we make a new monad where the values `10` and `3` had been added
150150

151151
To use `ap(..)`, we said we first need to construct a monad that holds a function. Specifically, we need one that holds a function that itself holds (remembers via closure) the value in `A`. Let that sink in for a moment.
152152

153-
To make a monad from `A` that holds a value-containing function, we'll call `A.map(..)`, giving it a curried function that "remembers" that extracted value (see Chapter 3) as its first argument. We'll call this new function-containing monad `C`:
153+
To make a monad from `A` that holds a value-containing function, we'll call `A.map(..)`, giving it a curried function that "remembers" that extracted value (see [Chapter 3](ch3.md/#one-at-a-time)) as its first argument. We'll call this new function-containing monad `C`:
154154

155155
```js
156156
function sum(x,y) { return x + y; }
@@ -187,7 +187,7 @@ var D = B.map( A.chain( curry( sum ) ) );
187187
D.inspect(); // Just(13);
188188
```
189189

190-
And that of course is just a composition (see Chapter 4):
190+
And that of course is just a composition (see [Chapter 4](ch4.md)):
191191

192192
```js
193193
var D = compose( B.map, A.chain, curry )( sum );
@@ -249,7 +249,7 @@ Maybe.of( someObj )
249249
.map( console.log );
250250
```
251251

252-
In other words, if at any point in the chain we get a `null`/`undefined` value, the Maybe magically switches into no-op mode -- it's now a `Nothing()` monad instance! -- and stops doing anything for the rest of the chain. That makes the nested property access safe against throwing JS exceptions if some property is missing/empty. That's cool, and a nice helpful abstraction for sure!
252+
In other words, if at any point in the chain we get a `null`/`undefined` value, the Maybe magically switches into no-op mode -- it's now a `Nothing()` monad instance! -- and stops doing anything for the rest of the chain. That makes the nested-property access safe against throwing JS exceptions if some property is missing/empty. That's cool, and a nice helpful abstraction for sure!
253253

254254
But... that approach to Maybe is not a pure monad.
255255

@@ -259,7 +259,7 @@ The earlier implementation of the Maybe monad I provided differs from other Mayb
259259

260260
So wait. If we don't get the automatic short-circuting, why is Maybe useful at all?!? That seems like its whole point.
261261

262-
Never fear! We can simply provide the empty-check externally, and the rest of the short-circuting behavior of the Maybe monad will work just fine. Here's how you could do the `someObj.something.else.entirely` nested-property access from before, but more "correctly":
262+
Never fear! We can simply provide the empty-check externally, and the rest of the short-circuting behavior of the Maybe monad will work just fine. Here's how you could do the nested-property access (`someObj.something.else.entirely`) from before, but more "correctly":
263263

264264
```js
265265
function isEmpty(val) {

0 commit comments

Comments
 (0)