├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── LICENSE.txt
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── apA.md
├── apB.md
├── apC.md
├── ch1.md
├── ch10.md
├── ch11-code
├── fp-helpers.js
├── index.html
├── mock-server.js
├── stock-ticker-events.js
├── stock-ticker.css
└── stock-ticker.js
├── ch11.md
├── ch2.md
├── ch3.md
├── ch4.md
├── ch5.md
├── ch6.md
├── ch7.md
├── ch8.md
├── ch9.md
├── fig1.png
├── fig10.png
├── fig10.svg
├── fig11.png
├── fig11.svg
├── fig12.png
├── fig12.svg
├── fig13.png
├── fig13.svg
├── fig14.png
├── fig14.svg
├── fig15.png
├── fig15.svg
├── fig16.png
├── fig16.svg
├── fig17.png
├── fig17.svg
├── fig18.png
├── fig18.svg
├── fig2.png
├── fig2.svg
├── fig3.png
├── fig3.svg
├── fig4.png
├── fig4.svg
├── fig5.png
├── fig5.svg
├── fig6.png
├── fig6.svg
├── fig7.png
├── fig8.png
├── fig8.svg
├── fig9.png
├── fig9.svg
├── foreword.md
├── preface.md
└── toc.md
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Please feel free to contribute to the quality of this content by submitting PR's for improvements to code snippets, explanations, etc. If there's any doubt or if you think that a word/phrase is used confusingly, **before submitting a PR, open an issue to ask about it.**
4 |
5 | However, if you choose to contribute content (not just typo corrections) to this repo, you agree that you're giving me a non-exclusive license to use that content for the book, as I (and any future publisher) deem appropriate. You probably guessed that already, but I just have to make sure the lawyers are happy by explicitly stating it.
6 |
7 | ## Search First!
8 |
9 | If you have any questions or concerns, please make sure to search the issues (both open and closed!) first, to keep the churn of issues to a minimum. I want to keep my focus on writing the book as much as possible.
10 |
11 | ## Typos?
12 |
13 | This book will go through official editing eventually, and typos will likely all be caught at that stage. As such, **typos are not a big concern of mine for now**.
14 |
15 | If you're going to submit a PR for typo fixes, please be measured in doing so by collecting several small changes into a single PR (in separate commits). Or, **just don't even worry about them for now,** because we'll get to them later. I promise.
16 |
17 | ## Reading Experience (Chapter/Section links, etc)
18 |
19 | I understand that reading one long .md file, with no relative cross links to other sections/etc, is not the preferred reading experience for most of you. As such, it's totally reasonable to want to file an issue/PR to add those kinds of features.
20 |
21 | This topic has been brought up many times, and I've considered it. For now, I **do not** accept these kinds of changes into the repo.
22 |
23 | The main purpose of my book repos is to track and manage the content for the purposes of publication (paid-for ebooks and print books). I do this in the open because I also care about providing free and early access to the content, to make sure there is no paywall barrier to learning.
24 |
25 | As such, this repo **is not optimized for your reading experience.** It's optimized for the publishing process.
26 |
27 | The primary intended reading experience -- likely the most pleasant one for many of you! -- is the ebooks or print books, which I (will eventually) sell. The balance I'm striking here is releasing the content for free, but selling the reading experience. Other authors make different decisions on that balance, but that's what I've come to for now.
28 |
29 | I hope you continue to enjoy and benefit from the content, and I also hope you value it enough to purchase the best reading experience in the ebook/print form (once available).
30 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/Functional-Light-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line *IF YOU ACTUALLY READ THEM*).
2 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/Functional-Light-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line *IF YOU ACTUALLY READ THEM*).
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript (book)
2 |
3 | This book explores the core principles of functional programming (FP) as they are applied to JavaScript. But what makes this book different is that we approach these principles without drowning in all the heavy terminology. We look at a subset of FP foundational concepts that I call "Functional-Light Programming" (FLP) and apply it to JavaScript.
4 |
5 | **Note:** Despite the word "Light" in the title, I do not consider or recommend this book as a "beginner", "easy", or "intro" book on the topic. This book is rigorous and full of gritty detail; it expects a solid foundation of JS knowledge before diving in. "Light" means limited in scope; instead of being more broad, this book goes much deeper into each topic than you typically find in other FP-JavaScript books.
6 |
7 | Let's face it: unless you're already a member of the FP cool kids club (I'm not!), a statement like, "a monad is just a monoid in the category of endofunctors", just doesn't mean anything useful to us.
8 |
9 | That's not to say the terms are meaning*less* or that FPrs are bad for using them. Once you graduate from Functional-Light, you'll maybe/hopefully want to study FP more formally, and you'll certainly have plenty of exposure to what they mean and why.
10 |
11 | But I want you to be able to apply some of the fundamentals of FP to your JavaScript *now*, because I believe it will help you write better, more *reason*able code.
12 |
13 | **To read more about the motivations and perspective behind this book, check out the [Preface](preface.md).**
14 |
15 | ## Book
16 |
17 | [Table of Contents](toc.md)
18 |
19 | * Foreword (TBA)
20 | * [Preface](preface.md)
21 | * [Chapter 1: Why Functional Programming?](ch1.md)
22 | * [Chapter 2: Foundations of Functional Functions](ch2.md)
23 | * [Chapter 3: Managing Function Inputs](ch3.md)
24 | * [Chapter 4: Composing Functions](ch4.md)
25 | * [Chapter 5: Reducing Side Effects](ch5.md)
26 | * [Chapter 6: Value Immutability](ch6.md)
27 | * [Chapter 7: Closure vs Object](ch7.md)
28 | * [Chapter 8: List Operations](ch8.md)
29 | * [Chapter 9: Recursion](ch9.md)
30 | * [Chapter 10: Functional Async](ch10.md)
31 | * [Chapter 11: Putting It All together](ch11.md)
32 | * [Appendix A: Transducing](apA.md)
33 | * [Appendix B: The Humble Monad](apB.md)
34 | * [Appendix C: FP Libraries](apC.md)
35 |
36 | ## Publishing
37 |
38 | I'm self-publishing this book, most likely digitally [on Leanpub](https://leanpub.com/fljs/). I'll also be trying to work out an option to sell print book copies, but that part is still uncertain.
39 |
40 | If you'd like to contribute financially towards the effort (or any of my other OSS work) aside from purchasing the books, I do have a [patreon](https://www.patreon.com/getify) that I would always appreciate your generosity towards.
41 |
42 | [](https://www.patreon.com/getify)
43 |
44 | ## In-person Training
45 |
46 | The content for this book derives heavily from a training workshop I teach professionally (in both public and private-corporate workshop format) of the same name.
47 |
48 | If you like this content and would like to contact me regarding conducting training on this, or other various JS/HTML5/Node.js topics, please reach out to me through any of these channels listed here:
49 |
50 | [http://getify.me](http://getify.me)
51 |
52 | ## Online Video Training
53 |
54 | I also have several JS training courses available in on-demand video format. I teach courses through [Frontend Masters](https://FrontendMasters.com), like my [Functional-Lite JS](https://frontendmasters.com/courses/functional-js-lite/) workshop. Some of those courses are also available on [PluralSight](https://www.pluralsight.com/search?q=kyle%20simpson&categories=all).
55 |
56 | ## Contributions
57 |
58 | Any contributions you make to this effort **are of course greatly appreciated**.
59 |
60 | But **PLEASE** read the [Contributions Guidelines](CONTRIBUTING.md) carefully before submitting a PR.
61 |
62 | ## License & Copyright
63 |
64 | The materials herein are all (c) 2016-2017 Kyle Simpson.
65 |
66 | This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 4.0 Unported License.
67 |
--------------------------------------------------------------------------------
/apB.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript
2 | # Appendix B: The Humble Monad
3 |
4 | Let me just start off this appendix by admitting: I did not know much about what a monad was before starting to write the following. 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)!
5 |
6 | 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.
7 |
8 | We're basically ending this book with a brief glimpse at monads, whereas most other FP literature kinda almost starts with monads! I do not encounter in my "functional light" programming much of a need to think explicitly in terms of monads, so that's why this material is more bonus than main core. But that's not to say monads aren't useful or prevalent -- they very much are.
9 |
10 | There's a bit of a joke around the JavaScript FP world that pretty much everybody has to write their own tutorial or blog post on what a monad is, like the writing of it alone is some rite-of-passage. Over the years, monads have variously been depicted as burritos, onions, and all sorts of other wacky conceptual abstractions. I hope there's none of that silly business going on here!
11 |
12 | > A monad is just a monoid in the category of endofunctors.
13 |
14 | We started the preface with this quote, so it seems fitting we come back to it here. But no, we won't be talking about monoids, endofunctors, or category theory. That quote is not only condescending, but totally unhelpful.
15 |
16 | My only hope for what you get out of this discussion is to not be scared of the term monad or the concept anymore -- I have been, for years! -- and to be able to recognize them when you see them. You might, just maybe, even use them on occassion.
17 |
18 | ## Type
19 |
20 | There's a huge area of interest in FP that we've basically stayed entirely away from throughout this book: type theory. I'm not going to get very deep into type theory, because quite frankly I'm not qualified to do so. And you wouldn't appreciate it even if I did.
21 |
22 | But what I will say is that a monad is basically a value type.
23 |
24 | The number `42` has a value type (number!) that brings with it certain characteristics and capabilities that we rely on. The string `"42"` may look very similar, but it has a different purpose in our program.
25 |
26 | In object oriented programming, when you have a set of data (even a single discrete value) and you have some behavior you want to bundle with it, you create an object/class to represent that "type". Instances are then members of that type. This practice generally goes by the name "Data Structures".
27 |
28 | I'm going to use the notion of data structures very loosely here, and assert that we may find it useful in a program to define a set of behaviors and constraints for a certain value, and bundle them together with that value into a single abstraction. That way, as we work with one or more of those kinds of values in our program, their behaviors come along for free and will make working with them more convenient. And by convenient, I mean more declarative and approachable for the reader of your code!
29 |
30 | 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.
31 |
32 | Recall in Chapter 8 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.
33 |
34 | ## Loose Interface
35 |
36 | Actually, a monad isn't a single data type, it's really more like a related collection of data types. It's kind of an interface that's implemented differently depending on the needs of different values. Each implementation is a different type of monad.
37 |
38 | For example, you may read about the "Identity Monad", the "IO Monad", the "Maybe Monad", the "Either Monad", or a variety of others. Each of these has the basic monad behavior defined, but it extends or overrides the interactions according to the use cases for each different type of monad.
39 |
40 | It's a little more than an interface though, because it's not just the presence of certain API methods that makes an object a monad. There's a certain set of guarantees about the interactions of these methods that is necessary, to be monadic. These well-known invariants are critical to usage of monads improving readability by familiarity; otherwise, it's just an ad hoc data structure that must be fully read to be understood by the reader.
41 |
42 | As a matter of fact, there's not even just one single unified agreement on the names of these monadic methods, the way a true interface would mandate; a monad is more like a loose interface. Some people call a certain method `bind(..)`, some call it `chain(..)`, some call it `flatMap(..)`, etc.
43 |
44 | So a monad is an object data structure with sufficient methods (of practically any name or sort) that at a minimum satisfy the main behavioral requirements of the monad definition. Each kind of monad has a different kind of extension above the minimum. But, because they all have an overlap in behavior, using two different kinds of monads together is still straightforward and predictable.
45 |
46 | It's in that sense that monads are sort of like an interface.
47 |
48 | ## Maybe
49 |
50 | It's very common in FP material to cover well-known monads like Maybe. And actually, the Maybe monad is really a pairing of two other simpler monads: Just and Nothing.
51 |
52 | Since a monad is a type, you might think we'd define `Maybe` as a class to be instantiated. That's a valid way of doing it, but it introduces `this`-binding issues in the methods that I don't want to juggle; instead I'm going to stick with just a simple function / object approach.
53 |
54 | Here's a minimal implementation of Maybe:
55 |
56 | ```js
57 | var Maybe = { Just, Nothing, of/* aka: unit, pure */: Just };
58 |
59 | function Just(val) {
60 | return { map, chain, ap, inspect };
61 |
62 | // *********************
63 |
64 | function map(fn) { return Just( fn( val ) ); }
65 | // aka: bind, flatMap
66 | function chain(fn) { return fn( val ); }
67 | function ap(anotherMonad) { return anotherMonad.map( val ); }
68 |
69 | function inspect() {
70 | return `Just(${ val })`;
71 | }
72 | }
73 |
74 | function Nothing() {
75 | return { map: Nothing, chain: Nothing, ap: Nothing, inspect };
76 |
77 | // *********************
78 |
79 | function inspect() {
80 | return "Nothing";
81 | }
82 | }
83 | ```
84 |
85 | **Note:** The `inspect(..)` method is included here only for our demonstration purposes. It serves no direct role in the monadic sense.
86 |
87 | Don't worry if most of this doesn't make sense right now. We're not gonna obsess much over the details or the math/theory behind the design of the Monad. Instead, we'll focus more on illustrating what we can do with it.
88 |
89 | Any monad instances of both `Just(..)` and `Nothing()` will all have `map(..)`, `chain(..)` (also called `bind(..)` or `flatMap(..)`), and `ap(..)` methods, as do all monads. The purpose of these methods and their behavior is to provide a standardized way of multiple monad instances working together. You'll notice that whatever `val` value a `Just(..)` instance holds, it's never changed. All methods create new monad instances instead of mutating it.
90 |
91 | Maybe is the pairing of these two monads. If a value is non-empty, it's represented by an instance of `Just(..)`; if it's empty, it's represented by an instance of `Nothing()`. Notice there's no imposition here of what "empty" means -- your code gets to decide that. More on that in the next section.
92 |
93 | But the value of this kind of monad representation is that whether we have a `Just(..)` instance of a `Nothing()` instance, we'll use it the same. `Nothing()` instances have no-op definitions for all methods. So if such a monad instance shows up in our monadic operations, it has the effect of basically short-circuiting to ignore behavior.
94 |
95 | The power of the Maybe abstraction is to encapsulate that behavior/no-op duality implicitly.
96 |
97 | ### Different Maybes
98 |
99 | Many implementations of a JavaScript Maybe monad include a check (usually in `map(..)`) to see if the value is `null` / `undefined`, and skipping the behavior if so. In fact, Maybe is trumpeted as being valuable precisely because it sort of automatically short-circuits its behavior with the encapsulated empty-value check.
100 |
101 | Here's how Maybe is typically illustrated:
102 |
103 | ```js
104 | // instead of unsafe `console.log( someObj.something.else.entirely )`:
105 |
106 | Maybe.of( someObj )
107 | .map( prop( "something" ) )
108 | .map( prop( "else" ) )
109 | .map( prop( "entirely" ) )
110 | .map( console.log );
111 | ```
112 |
113 | 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!
114 |
115 | But... that approach to Maybe is not a pure monad.
116 |
117 | The core spirit of a Monad says that it must be valid for all values and cannot do any inspection of the value, at all -- not even a null check. So those other implementations are cutting corners for the sake of convenience. It's not a huge deal, but when it comes to learning something, you should probably learn it in its purest form first before you go bending the rules.
118 |
119 | The earlier implementation of the Maybe monad I provided differs from other Maybes primarily in that it does not have the null-check in it. Also, we present `Maybe` as a loose pairing of `Just(..)` / `Nothing()`.
120 |
121 | So wait. If we don't get the automatic short-circuting, why is Maybe useful at all?!? That seems like its whole point.
122 |
123 | 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 we'll do it more "correctly":
124 |
125 | ```js
126 | function isEmpty(val) {
127 | return val === null || val === undefined;
128 | }
129 |
130 | var safeProp = curry( function safeProp(prop,obj){
131 | if (isEmpty( obj[prop] )) return Maybe.Nothing();
132 | return Maybe.of( obj[prop] );
133 | } );
134 |
135 | Maybe.of( someObj )
136 | .chain( safeProp( "something" ) )
137 | .chain( safeProp( "else" ) )
138 | .chain( safeProp( "entirely" ) )
139 | .map( console.log );
140 | ```
141 |
142 | We made a `safeProp(..)` that does the empty-check, and selects either a `Nothing()` monad instance if so, or wraps the value in a `Just(..)` instance (via `Maybe.of(..)`). Then instead of `map(..)`, we use `chain(..)` which knows how to "unwrap" the monad that `safeProp(..)` returns.
143 |
144 | We get the same chain short-circuiting upon encountering an empty value. We just don't embed that logic into the Maybe.
145 |
146 | The benefit of the monad, and Maybe specifically, is that our `map(..)` and `chain(..)` methods have a consistent and predictable interaction regardless of which kind of monad comes back. That's pretty cool!
147 |
148 | ## Humble
149 |
150 | Now that we have a little more understanding of Maybe and what it does, I'm going to put a little twist on it -- and add some self-deferential humor to our discussion -- by inventing the Maybe+Humble monad. Technically, `Humble(..)` is not a monad itself, but a factory function that produces a Maybe monad instance.
151 |
152 | Humble is an admittedly contrived data structure wrapper that uses Maybe to track the status of an `egoLevel` number. Specifically, `Humble(..)`-produced monad instances only operate if their ego level value is low enough (less than `42`!) to be considered humble; otherwise it's a `Nothing()` no-op. That should sound a lot like Maybe; it's pretty similar!
153 |
154 | Here's the factory function for our Maybe+Humble monad:
155 |
156 | ```js
157 | function Humble(egoLevel) {
158 | // accept anything other than a number that's 42 or higher
159 | return !(Number( egoLevel ) >= 42) ?
160 | Maybe.of( egoLevel ) :
161 | Maybe.Nothing();
162 | }
163 | ```
164 |
165 | You'll notice that this factory function is kinda like `safeProp(..)`, in that it uses a condition to decide if it should pick the `Just(..)` or the `Nothing()` part of the Maybe.
166 |
167 | Let's illustrate some basic usage:
168 |
169 | ```js
170 | var bob = Humble( 45 );
171 | var alice = Humble( 39 );
172 |
173 | bob.inspect(); // Nothing
174 | alice.inspect(); // Just(39)
175 | ```
176 |
177 | What if Alice wins a big award and is now a bit more proud of herself?
178 |
179 | ```js
180 | function winAward(ego) {
181 | return Humble( ego + 3 );
182 | }
183 |
184 | alice = alice.chain( winAward );
185 | alice.inspect(); // Nothing
186 | ```
187 |
188 | The `Humble( 39 + 3 )` call creates a `Nothing()` monad instance to return back from the `chain(..)` call, so now Alice doesn't qualify as humble anymore.
189 |
190 | Now, let's use a few monads them together:
191 |
192 | ```js
193 | var bob = Humble( 41 );
194 | var alice = Humble( 39 );
195 |
196 | var teamMembers = curry( function teamMembers(ego1,ego2){
197 | console.log( `Our humble team's egos: ${ego1} ${ego2}` );
198 | } );
199 |
200 | bob.map( teamMembers ).ap( alice );
201 | // Our humble team's egos: 41 39
202 | ```
203 |
204 | Since `teamMembers(..)` is curried, the `bob.map(..)` call passes in the `bob` ego level (`41`), and creates a monad instance with the remaining function wrapped up. Calling `ap(alice)` on *that* monad calls `alice.map(..)` and passes to it the function from the monad. The effect is that both monad's values have been provided to `teamMembers(..)` function, printing out the message as shown.
205 |
206 | However, if either or both monads are actually `Nothing()` instances (because their ego level was too high):
207 |
208 | ```js
209 | var frank = Humble( 45 );
210 |
211 | bob.map( teamMembers ).ap( frank );
212 |
213 | frank.map( teamMembers ).ap( bob );
214 | ```
215 |
216 | `teamMembers(..)` never gets called (and no message is printed), because `frank` is a `Nothing()` instance. That's the power of the Maybe monad, and our `Humble(..)` factory allows us to select based on the ego level. Cool!
217 |
218 | ### Humility
219 |
220 | One more example to illustrate the behaviors of our Maybe+Humble data structure:
221 |
222 | ```js
223 | function introduction() {
224 | console.log( "I'm just a learner like you! :)" );
225 | }
226 |
227 | var egoChange = curry( function egoChange(amount,concept,egoLevel) {
228 | console.log( `${amount > 0 ? "Learned" : "Shared"} ${concept}.` );
229 | return Humble( egoLevel + amount );
230 | } );
231 |
232 | var learn = egoChange( 3 );
233 |
234 | var learner = Humble( 35 );
235 |
236 | learner
237 | .chain( learn( "closures" ) )
238 | .chain( learn( "side effects" ) )
239 | .chain( learn( "recursion" ) )
240 | .chain( learn( "map/reduce" ) )
241 | .map( introduction );
242 | // Learned closures.
243 | // Learned side effects.
244 | // Learned recursion.
245 | ```
246 |
247 | Unfortunately, the learning process seems to have been cut short. You see, I've found that learning a bunch of stuff without sharing with others: inflates your ego too much and is not good for your skills.
248 |
249 | Let's try a better approach:
250 |
251 | ```js
252 | var share = egoChange( -2 );
253 |
254 | learner
255 | .chain( learn( "closures" ) )
256 | .chain( share( "closures" ) )
257 | .chain( learn( "side effects" ) )
258 | .chain( share( "side effects" ) )
259 | .chain( learn( "recursion" ) )
260 | .chain( share( "recursion" ) )
261 | .chain( learn( "map/reduce" ) )
262 | .chain( share( "map/reduce" ) )
263 | .map( introduction );
264 | // Learned closures.
265 | // Shared closures.
266 | // Learned side effects.
267 | // Shared side effects.
268 | // Learned recursion.
269 | // Shared recursion.
270 | // Learned map/reduce.
271 | // Shared map/reduce.
272 | // I'm just a learner like you! :)
273 | ```
274 |
275 | Sharing while you learn. That's the best way to learn more and learn better.
276 |
277 | ## Summary
278 |
279 | What is a monad, anyway?
280 |
281 | A monad is a value type, an interface, an object data structure with encapsulated behaviors.
282 |
283 | But none of those definitions are particularly useful. Here's an attempt at something better: a monad is how you organize behavior around a value in a more declarative way.
284 |
285 | As with everything else in this book, use monads where they are helpful but don't use them just because everyone else talks about them in FP. Monads aren't a universal silver bullet, but they do offer some utility when used conservatively.
286 |
--------------------------------------------------------------------------------
/apC.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript
2 | # Appendix C: FP Libraries
3 |
4 | If you've been reading this book from start to finish, take a minute to stop and look back how far you've come since Chapter 1. It's been quite a journey. I hope you've learned a lot and gained insight into thinking functionally for your own programs.
5 |
6 | I want to close this book leaving you with some quick pointers of working with official FP libraries. This is not an exhaustive documentation on each, but a quick glance at the things you should be aware of as you venture beyond "functional-light" into true FP.
7 |
8 | Wherever possible, I recommend you *not* reinvent any wheels. If you find an FP library that suits your needs, use it. Only use the ad hoc helper utilities from this book -- or invent ones of your own! -- if you can't find a suitable library method for your circumstance.
9 |
10 | ## Stuff To Investigate
11 |
12 | Let's expand the list of FP libraries to be aware of, from Chapter 1. We won't cover all of these (as there's a lot of overlap), but here are the ones that should probably be on your radar screen:
13 |
14 | * [Ramda](http://ramdajs.com): General FP Utilities
15 | * [Sanctuary](https://github.com/sanctuary-js/sanctuary): Ramda Companion For FP Types
16 | * [lodash/fp](https://github.com/lodash/lodash/wiki/FP-Guide): General FP Utilities
17 | * [functional.js](http://functionaljs.com/): General FP Utilities
18 | * [Immutable](https://github.com/facebook/immutable-js): Immutable Data Structures
19 | * [Mori](https://github.com/swannodette/mori): (ClojureScript Inspired) Immutable Data Structures
20 | * [Seamless-Immutable](https://github.com/rtfeldman/seamless-immutable): Immutable Data Helpers
21 | * [tranducers-js](https://github.com/cognitect-labs/transducers-js): Transducers
22 | * [monet.js](https://github.com/cwmyers/monet.js): Monadic Types
23 |
24 | There are dozens of other fine libraries not on this list. Just because it's not on my list here doesn't mean it's not good, nor is this list a particular endorsement. It's just a quick glance at the landscape of FP-in-JavaScript. A much longer list of FP resources can be [found here](https://github.com/stoeffel/awesome-fp-js).
25 |
26 | One resource that's extremely important to the FP world -- it's not a library but more an encyclopedia! -- is [Fantasy Land](https://github.com/fantasyland/fantasy-land) (aka FL).
27 |
28 | This is definitely not light reading for the faint of heart. It's a complete detailed roadmap of all of FP as it's interpreted in JavaScript. FL has become a de facto standard for JavaScript FP libraries to adhere to, to ensure maximum interoperability.
29 |
30 | Fantasy Land is pretty much the exact opposite of "functional-light". It's the full-on no holds barred approach to FP in JavaScript. That said, as you venture beyond this book, it's undeniable that FL will be down that road for you. I'd recommend you bookmark it, and go back to it after you've had at least 6 months of real world practice with this book's concepts.
31 |
32 | ## Ramda (0.23.0)
33 |
34 | From the [Ramda documentation](http://ramdajs.com/):
35 |
36 | > Ramda functions are automatically curried.
37 | >
38 | > The parameters to Ramda functions are arranged to make it convenient for currying. The data to be operated on is generally supplied last.
39 |
40 | I find that design decision to be one of Ramda's strengths. It's also important to note that Ramda's form of currying (as with most libraries, it seems) is the "loose currying" we talked about in Chapter 3.
41 |
42 | The final example of Chapter 3 -- recall defining a point-free `printIf(..)` utility -- can be done with Ramda like this:
43 |
44 | ```js
45 | function output(msg) {
46 | console.log( msg );
47 | }
48 |
49 | function isShortEnough(str) {
50 | return str.length <= 5;
51 | }
52 |
53 | var isLongEnough = R.complement( isShortEnough );
54 |
55 | var printIf = R.partial( R.flip( R.when ), [output] );
56 |
57 | var msg1 = "Hello";
58 | var msg2 = msg1 + " World";
59 |
60 | printIf( isShortEnough, msg1 ); // Hello
61 | printIf( isShortEnough, msg2 );
62 |
63 | printIf( isLongEnough, msg1 );
64 | printIf( isLongEnough, msg2 ); // Hello World
65 | ```
66 |
67 | A few differences to point out compared to Chapter 3's approach:
68 |
69 | * We use `R.complement(..)` instead of `not(..)` to create a negating function `isLongEnough(..)` around `isShortEnough(..)`.
70 |
71 | * We use `R.flip(..)` instead of `reverseArgs(..)`. It's important to note that `R.flip(..)` only swaps the first two arguments, whereas `reverseArgs(..)` reverses all of them. In this case, `flip(..)` is more convenient for us, so we don't need to do `partialRight(..)` or any of that kind of juggling.
72 |
73 | * `R.partial(..)` takes all of its subsequent arguments (beyond the function) as a single array.
74 |
75 | * Because Ramda is using loose currying, we don't need to use `R.uncurryN(..)` to get a `printIf(..)` that takes both its arguments. If we did, it would look like `R.uncurryN( 2, .. )` wrapped around the `R.partial(..)` call; but, that's not necessary.
76 |
77 | Ramda is a very popular and powerful library. It's a really good place to start if you're practicing adding FP to your code base.
78 |
79 | ## Lodash/fp (4.17.4)
80 |
81 | Lodash is one of the most popular libraries in the entire JS ecosystem. They publish an "FP friendly" version of their API as ["lodash/fp"](https://github.com/lodash/lodash/wiki/FP-Guide).
82 |
83 | In Chapter 8, we looked at composing standalone list operations (`map(..)`, `filter(..)`, and `reduce(..)`). Here's how we could do it with "lodash/fp":
84 |
85 | ```js
86 | var sum = (x,y) => x + y;
87 | var double = x => x * 2;
88 | var isOdd = x => x % 2 == 1;
89 |
90 | fp.compose( [
91 | fp.reduce( sum )( 0 ),
92 | fp.map( double ),
93 | fp.filter( isOdd )
94 | ] )
95 | ( [1,2,3,4,5] ); // 18
96 | ```
97 |
98 | Instead of the more familiar `_.` namespace prefix, "lodash/fp" defines its methods with `fp.` as the namespace prefix. I find that a helpful distinguisher, and also generally more easy on my eyes than `_.` anyway!
99 |
100 | Notice that `fp.compose(..)` (also known as `_.flowRight(..)` in lodash proper) takes an array of functions instead of individual arguments.
101 |
102 | You cannot beat the stability, widespread community support, and performance of lodash. It's a solid bet for your FP explorations.
103 |
104 | ## Mori (0.3.2)
105 |
106 | In Chapter 6, we already briefly glanced at the Immutable.js library, probably the most well-known for immutable data structures.
107 |
108 | Let's instead look at another popular library: [Mori](https://github.com/swannodette/mori). Mori is designed with a different (ostensibly more FP-like) take on API: it uses standalone functions instead of methods directly on the values.
109 |
110 | ```js
111 | var state = mori.vector( 1, 2, 3, 4 );
112 |
113 | var newState = mori.assoc(
114 | mori.into( state, Array.from( {length: 39} ) ),
115 | 42,
116 | "meaning of life"
117 | );
118 |
119 | state === newState; // false
120 |
121 | mori.get( state, 2 ); // 3
122 | mori.get( state, 42 ); // undefined
123 |
124 | mori.get( newState, 2 ); // 3
125 | mori.get( newState, 42 ); // "meaning of life"
126 |
127 | mori.toJs( newState ).slice( 1, 3 ); // [2,3]
128 | ```
129 |
130 | Some interesting things to point out about Mori for this example:
131 |
132 | * We're using a `vector` instead of a `list` (as one might assume), mostly because the documentation says it behaves more like we expect JS arrays to be.
133 |
134 | * We cannot just randomly set a position past the end of the vector like we can with JS arrays; that throws an exception. So we have to first "grow" the vector using `mori.into(..)` with an array of the appropriate size of extra slots we want. Once we have a vector with 43 slots (4 + 39), we can set the final slot (position `42`) to the `"meaning of life"` value using the `mori.assoc(..)` method.
135 |
136 | * The intermediate step of creating a larger vector with `mori.into(..)` and then creating another from it with `mori.assoc(..)` might sound inefficient. But the beauty of immutable data structures is that no cloning is going on here. Each time a "change" is made, the new data structure is just tracking the difference from the previous state.
137 |
138 | Mori is heavily inspired by ClojureScript. Its API will be very familiar if you have experience (or currently work in!) that language. Since I don't have that experience, I find the method names a little strange to get used to.
139 |
140 | But I really like the standalone function design instead of methods on values. Mori also has some functions that automatically return regular JS arrays, which is a nice convenience.
141 |
142 | ## Summary
143 |
144 | JavaScript is not particularly designed as an FP language. However, it does have enough of the basics (like function values, closures, etc) for us to make it FP-friendly. And the libraries we've examined here will help you do that.
145 |
146 | Armed with the concepts from this book, you're ready to start tackling real world code. Find a good FP library and jump in. Practice, practice, practice!
147 |
148 | So... that's it. I've shared what I have for you, for now. I hereby officially certify you as a "Functional-Light JavaScript" programmer! It's time to close out this "chapter" of our story of learning FP together. But my learning journey continues; I hope your's does, too!
149 |
--------------------------------------------------------------------------------
/ch1.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript
2 | # Chapter 1: Why Functional Programming?
3 |
4 | > Functional programmer: (noun) One who names variables "x", names functions "f", and names code patterns "zygohistomorphic prepromorphism"
5 | >
6 | > James Iry @jamesiry 5/13/15
7 | >
8 | > https://twitter.com/jamesiry/status/598547781515485184
9 |
10 | Functional Programming (FP) is not a new concept by any means. It's been around almost the entire history of programming. However -- and I'm not sure it's fair to say, but! -- it sure hasn't seemed like as mainstream of a concept in the overall developer world until perhaps the last few years. I think FP has more been the realm of academics.
11 |
12 | But that's all changing. A groundswell of interest is growing around FP, not just at the languages level but even in libraries and frameworks. You very well might be reading this text because you've finally realized FP is something you can't ignore any longer. Or maybe you're like me and you've tried to learn FP many times before but struggled to wade through all the terms or mathematical notation.
13 |
14 | Whatever your reason for reading this book, welcome to the party!
15 |
16 | ## Confidence
17 |
18 | I have a very simple premise that sort of underlies everything I do as a teacher of software development (in JavaScript): code that you cannot trust is code that you do not understand. And furthermore, if you cannot trust or understand your code, then you can have no confidence whatsoever that the code you write is suitable to the task. You run the program and cross your fingers.
19 |
20 | What do I mean by trust? I mean that you can verify, by reading, not just running, that you understand what a piece of code *will* do, not just relying on what it *should* do. Perhaps more often than we should, we tend to rely on verification of our program's correctness by running test suites. I don't mean to suggest tests are bad. But I do think we should aspire to be able to understand our code well enough that we know the test suite will pass before it runs.
21 |
22 | I believe the techniques that form the foundation of FP are designed from the mindset of having far more confidence over our programs just by reading them. Someone who understands FP, and who diligently uses it in their programs, will write code that they can read and verify, by the principles they have already proven to be true, that the program will do what they want.
23 |
24 | It is my hope that you will begin to develop more confidence in the code you write, and that these functional-light programming principles will begin your journey in that direction.
25 |
26 | ## Communication
27 |
28 | Why is functional programming important? To answer that, we need to take a bigger step back and talk about why programming itself is important.
29 |
30 | It may surprise you to hear this, but I don't believe that code is primarily a set of instructions for the computer. As a matter of fact, I think the fact that code instructs the computer is almost a happy accident.
31 |
32 | I believe very deeply that the vastly more important role of code is as a means of communication with other human beings.
33 |
34 | You probably know by experience that an awful lot of your time spent "coding" is actually spent reading existing code. Very few of us are so privileged as to spend all or most of our time simply banging out all new code and never dealing with code that others (or our past selves) wrote.
35 |
36 | Did you know that researchers who've studied this topic say that 70% of the time we spend maintaining code is just spent reading it to understand it? I find that shocking. 70%. No wonder the global average for a programmer's lines of code written per day is around 5. We spend the other 7 hours and 30 minutes of the day just reading the code to figure out where those 5 lines should go!
37 |
38 | I think we should focus a lot -- a LOT! -- more on the readability of our code. Like, a lot more. And by the way, readability is not about least number of characters. Readability is actually most affected by familiarity (yes, that's been studied, too).
39 |
40 | So, if we are going to spend more time concerned with making code that will be more readable and understandable, FP turns out to be a really handy pattern in that effort. The principles of FP are well established, deeply studied and vetted, and provably verifiable.
41 |
42 | If we use FP principles, I believe we will create code that is easier to reason about. Once we know these principles, they will be recognizable and familiar in the code, meaning we'll spend less time figuring that part out when we read a piece of code. Our focus will be spent on how all the well-known, established lego pieces are assembled, not on what those lego pieces mean.
43 |
44 | FP (at least, without all the terminology weighing it down) is one of the most effective tools for writing readable code. *That* is why it's so important.
45 |
46 | ### Readability Curve
47 |
48 | It's really important I take a moment to call out a phenomena that has derailed and frustrated me many times over the years, and was especially acute while writing this book.
49 |
50 | I also think it's probably something that many developers have a tendency to run into. You, dear reader, may just find yourself in this same boat as you work through this text. But take heart; if you stick this out, the curve comes back.
51 |
52 |
53 |
54 |
55 |
56 | We'll cover this more in the next chapter, but imperative code is the code you probably already write, like `if` statements and `for`-loops. It's focused on precisely instructing the computer *how* to do something. Declarative code, and moreover the kind of code we'll be striving to learn to write that adheres to FP principles, is code that's more focused on describing the *what* outcome.
57 |
58 | Let me just clue you into a painful fact, one that I've been trying to swallow the whole time I've worked on this book: it takes a lot more effort, and often a lot more code, to improve the readability of your code and to minimize or eliminate most of the places where you might write bugs.
59 |
60 | If you're hoping that your FP code refactoring will immediately make your code more graceful, elegant, clever, and concise, that's just not a realistic expectation -- at least not at first.
61 |
62 | FP is a very different way of thinking about how code should be structured, to make the flow of data much more obvious and to help your reader follow your thinking. This effort is eminently worthwhile, but it can be an arduous journey and the code you end up with will not seem more readable until you've spent a lot more time conditioning yourself to FP.
63 |
64 | Also, my experience has been that it takes about a half dozen attempts at turning a snippet of imperative code into more declarative FP, before I end up with something that's clear enough for me to understand. For me, writing FP is more like a process than a binary flip from one paradigm to another.
65 |
66 | I also apply the "read it later" test to every piece of FP code I write. I write it, then leave the code alone for a few hours or a day, then come back and try to read it with fresh eyes. Usually, it's confusing as hell, so I tweak it and repeat.
67 |
68 | Functional programming has not been for me achieving a single elegant brush stroke on the artist's canvas that leaves the audience in awe. Rather, it's a painstaking, detailed, sometimes treacherous hack through the weeds of a neglected field.
69 |
70 | But I'm not trying to dampen your spirits. I really want you to hack through those weeds. I am glad I have. I can finally start to see the curve bending upward towards more readability. The effort has been worth it. I believe it will be for you, too.
71 |
72 | ## Take
73 |
74 | We're going to approach FP from the ground up, and uncover the basic foundational principles that I believe formal FPers would admit are the scaffolding for everything they do. But for the most part we'll stay arms length away from most of the intimidating terminology or mathematical notation that can so easily frustrate learners.
75 |
76 | I believe it's less important what you call something and more important that you understand what it is and how it works. That's not to say there's no importance to shared terminology -- it undoubtedly eases communication among seasoned professionals. But for the learner, I've found it can be somewhat distracting.
77 |
78 | So I hope this book can focus more on the base concepts and less on the fancy terminology. That's not to say there won't be terminology; there definitely will be. But don't get too wrapped up in the fancier words. Look beyond them to the ideas. That's what this book is trying to be about.
79 |
80 | I call the less formal practice herein "Functional-Light Programming" because I think where the formalism of true FP suffers is that it can be quite overwhelming if you're not already accustomed to formal thought. I'm not just guessing; this is my own personal story. Even after teaching FP and writing this book, I can still say that the formalism of terms and notation in FP is very, very diffcult for me to process. I've tried, and tried, and I can't seem to get through much of it.
81 |
82 | I know many FPers who believe that the formalism itself helps learning. But I think there's clearly a cliff where that only becomes true once you reach a certain comfort with the formalism. If you happen to already have a math background or even some flavors of CS experience, this may come more naturally to you. But some of us don't, and no matter how hard we try, the formalism keeps getting in the way.
83 |
84 | So this book introduces the concepts that I believe FP is built on, but comes at it by giving you a boost to climb up the cliff wall rather than throwing you straight at it to figure out how to climb as you go.
85 |
86 | ## YAGNI
87 |
88 | If you've been around programming for very long, chances are you've heard the phrase "YAGNI" before: "You Ain't Gonna Need It". This principle primarily comes from extreme programming, and stresses the high risk and cost of building a feature before it's needed.
89 |
90 | Sometimes we guess we'll need a feature in the future, build it now believing it'll be easier to do as we build other stuff, then realize we guessed wrong and the feature wasn't needed, or needed to be quite different. Other times we guess right, but build a feature too early, and suck up time from the features that are genuinely needed now; we incur an opportunity cost in diluting our energy.
91 |
92 | YAGNI challenges us to remember: even if it's counter intuitive in a situation, we often should postpone building something until it's presently needed. We tend to exaggerate our mental estimates of the future refactoring cost of adding it later when it is needed. Odds are, it won't be as hard to do later as we might assume.
93 |
94 | As it applies to functional programming, I would give this admonition: there will be plenty of interesting and compelling patterns discussed in this text, but just because you find some pattern exciting to apply, it may not necessarily be appropriate to do so in a given part of your code.
95 |
96 | This is where I will differ from many more formal FPers: just because you *can* FP something doesn't mean you *should* FP it. Moreover, there are many ways to slice a problem, and even though you may have learned a more sophisticated approach that is more "future-proof" to maintenance and extensibility, a simpler FP pattern might be more than sufficient in that spot.
97 |
98 | Generally, I'd recommend to seek balance in what you code, and to be conservative in your application of FP concepts as you get the hang of things. Default to the YAGNI principle in deciding if a certain pattern or abstraction will help that part of the code be more readable or if it's just introducing more clever sophistication that isn't (yet) warranted.
99 |
100 | > Reminder, any extensibility point that’s never used isn’t just wasted effort, it’s likely to also get in your way as well
101 | >
102 | > Jeremy D. Miller @jeremydmiller 2/20/15
103 | >
104 | > https://twitter.com/jeremydmiller/status/568797862441586688
105 |
106 | Remember, every single line of code you write has a reader cost associated with it. That reader may be another team member, or even your future self. Neither of those readers will be impressed with overly clever unnecessary sophistication to show off your FP agility.
107 |
108 | The best code is the code that is most readable in the future because it strikes exactly the right balance between what it can/should be (idealism) and what it must be (pragmatism).
109 |
110 | ## Resources
111 |
112 | I have drawn on a great many different resources to be able to compose this text. I believe you too may benefit from them, so I wanted to take a moment to point them out.
113 |
114 | ### Books
115 |
116 | Some FP/JavaScript books that you should definitely read:
117 |
118 | * [Professor Frisby's Mostly Adequate Guide to Functional Programming](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch1.html) by [Brian Lonsdorf](https://twitter.com/drboolean)
119 | * [JavaScript Allongé](https://leanpub.com/javascript-allonge) by [Reg Braithwaite](https://twitter.com/raganwald)
120 | * [Functional JavaScript](http://shop.oreilly.com/product/0636920028857.do) by [Michael Fogus](https://twitter.com/fogus)
121 |
122 | ### Blogs/Sites
123 |
124 | Some other authors and content you should check out:
125 |
126 | * [Fun Fun Function Videos](https://www.youtube.com/watch?v=BMUiFMZr7vk) by [Mattias P Johansson](https://twitter.com/mpjme)
127 | * [Awesome FP JS](https://github.com/stoeffel/awesome-fp-js)
128 | * [Kris Jenkins](http://blog.jenkster.com/2015/12/what-is-functional-programming.html)
129 | * [Eric Elliott](https://medium.com/@_ericelliott)
130 | * [James A Forbes](https://james-forbes.com/)
131 | * [James Longster](https://github.com/jlongster)
132 | * [André Staltz](http://staltz.com/)
133 | * [Functional Programming Jargon](https://github.com/hemanth/functional-programming-jargon#functional-programming-jargon)
134 | * [Functional Programming Exercises](https://github.com/InceptionCode/Functional-Programming-Exercises)
135 |
136 | ### Libraries
137 |
138 | The code snippets in this book do not use libraries. Each operation that we discover, we'll derive how to implement it in standalone, plain ol' JavaScript. However, as you begin to build more of your real code with FP, you'll quickly want a library to provide optimized and highly reliable versions of these commonly accepted utilities.
139 |
140 | By the way, you'll want to make sure you check the documentation for the library functions you use to make sure you know how they work. There will be a lot of similarities in many of them to the code we build on in this text, but there will undoubtedly be some differences, even between popular libraries.
141 |
142 | Here are a few popular FP libraries for JavaScript that are a great place to start your exploration with:
143 |
144 | * [Ramda](http://ramdajs.com)
145 | * [lodash/fp](https://github.com/lodash/lodash/wiki/FP-Guide)
146 | * [functional.js](http://functionaljs.com/)
147 | * [Immutable.js](https://github.com/facebook/immutable-js)
148 |
149 | Appendix C illustrates some of these libraries using various examples from the text.
150 |
151 | ## Summary
152 |
153 | This is Functional-Light JavaScript. The goal is to learn to communicate with our code but not suffocate under mountains of notation or terminology to get there. I hope this book jumpstarts your journey!
154 |
--------------------------------------------------------------------------------
/ch11-code/fp-helpers.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // curried list operators
4 | var map = unboundMethod( "map", 2 );
5 | var filter = unboundMethod( "filter", 2 );
6 | var filterIn = filter;
7 | var reduce = unboundMethod( "reduce", 3 );
8 | var each = unboundMethod( "forEach", 2 );
9 | var flatMap = curry( function flatMap(mapperFn,arr) {
10 | return arr.reduce( function reducer(list,v) {
11 | return list.concat( mapperFn( v ) );
12 | }, [] );
13 | } );
14 |
15 |
16 | // ************************************
17 |
18 | function filterOut(predicateFn,arr) {
19 | return filterIn( not( predicateFn ), arr );
20 | }
21 |
22 | function not(predicate) {
23 | return function negated(...args){
24 | return !predicate( ...args );
25 | };
26 | }
27 |
28 | function reverseArgs(fn) {
29 | return function argsReversed(...args){
30 | return fn( ...args.reverse() );
31 | };
32 | }
33 |
34 | function spreadArgs(fn) {
35 | return function spreadFn(argsArr) {
36 | return fn( ...argsArr );
37 | };
38 | }
39 |
40 | function partial(fn,...presetArgs) {
41 | return function partiallyApplied(...laterArgs){
42 | return fn( ...presetArgs, ...laterArgs );
43 | };
44 | }
45 |
46 | function curry(fn,arity = fn.length) {
47 | return (function nextCurried(prevArgs){
48 | return function curried(nextArg){
49 | var args = prevArgs.concat( [nextArg] );
50 |
51 | if (args.length >= arity) {
52 | return fn( ...args );
53 | }
54 | else {
55 | return nextCurried( args );
56 | }
57 | };
58 | })( [] );
59 | }
60 |
61 | function uncurry(fn) {
62 | return function uncurried(...args){
63 | var ret = fn;
64 |
65 | for (let i = 0; i < args.length; i++) {
66 | ret = ret( args[i] );
67 | }
68 |
69 | return ret;
70 | };
71 | }
72 |
73 | function zip(arr1,arr2) {
74 | var zipped = [];
75 | arr1 = arr1.slice();
76 | arr2 = arr2.slice();
77 |
78 | while (arr1.length > 0 && arr2.length > 0) {
79 | zipped.push( [ arr1.shift(), arr2.shift() ] );
80 | }
81 |
82 | return zipped;
83 | }
84 |
85 | function compose(...fns) {
86 | return function composed(result){
87 | // copy the array of functions
88 | var list = fns.slice();
89 |
90 | while (list.length > 0) {
91 | result = list.pop()( result );
92 | }
93 |
94 | return result;
95 | };
96 | }
97 |
98 | function pipe(...fns) {
99 | return function piped(result){
100 | var list = fns.slice();
101 |
102 | while (list.length > 0) {
103 | result = list.shift()( result );
104 | }
105 |
106 | return result;
107 | };
108 | }
109 |
110 | function prop(name,obj) {
111 | return obj[name];
112 | }
113 |
114 | function setProp(name,obj,val) {
115 | var o = Object.assign( {}, obj );
116 | o[name] = val;
117 | return o;
118 | }
119 |
120 | function unboundMethod(methodName,argCount = 2) {
121 | return curry(
122 | (...args) => {
123 | var obj = args.pop();
124 | return obj[methodName]( ...args );
125 | },
126 | argCount
127 | );
128 | }
129 |
--------------------------------------------------------------------------------
/ch11-code/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Functional-Light JavaScript, Ch11: Stock Ticker
6 |
7 |
8 |
9 |
Functional-Light JavaScript, Ch11: Stock Ticker
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ch11-code/mock-server.js:
--------------------------------------------------------------------------------
1 | function connectToServer() {
2 | // faking an event emitter attached to a server-event stream
3 | return evtEmitter;
4 | }
5 |
6 |
7 | // ***********************************
8 | // MOCK SERVER
9 | // ***********************************
10 |
11 | // simple/mock event emitter
12 | var evtEmitter = {
13 | handlers: {},
14 | on(evtName,cb) {
15 | this.handlers[evtName] = this.handlers[evtName] || [];
16 | this.handlers[evtName][this.handlers[evtName].length] = cb;
17 | },
18 | addEventListener(...args) {
19 | return this.on( ...args );
20 | },
21 | removeEventListener(){},
22 | emit(evtName,...args) {
23 | for (let handler of (this.handlers[evtName] || [])) {
24 | handler(...args);
25 | }
26 | }
27 | };
28 |
29 | var stocks = {
30 | "AAPL": { price: 121.95, change: 0.01 },
31 | "MSFT": { price: 65.78, change: 1.51 },
32 | "GOOG": { price: 821.31, change: -8.84 },
33 | };
34 |
35 | setTimeout( function initialStocks(){
36 | for (let id in stocks) {
37 | // !!SIDE EFFECTS!!
38 | evtEmitter.emit( "stock", Object.assign( { id }, stocks[id] ) );
39 | }
40 | }, 100 );
41 |
42 | setTimeout( function randomStockUpdate(){
43 | var stockIds = Object.keys( stocks );
44 | var stockIdx = randInRange( 0, stockIds.length - 1 );
45 | var change = (randInRange( 1, 10 ) > 7 ? -1 : 1) *
46 | (randInRange( 1, 10 ) / 1E2);
47 |
48 | var newStock = Object.assign( stocks[stockIds[stockIdx]] );
49 | newStock.price += change;
50 | newStock.change += change;
51 |
52 | // !!SIDE EFFECTS!!
53 | stocks[stockIdx[stockIdx]] = newStock;
54 | evtEmitter.emit( "stock-update", Object.assign( { id: stockIds[stockIdx] }, newStock ) );
55 |
56 | setTimeout( randomStockUpdate, randInRange( 300, 1500 ) );
57 | }, 1000 );
58 |
59 |
60 | // !!SIDE EFFECTS!!
61 | function randInRange(min = 0,max = 1E9) {
62 | return (Math.round(Math.random() * 1E4) % (max - min)) + min;
63 | }
64 |
--------------------------------------------------------------------------------
/ch11-code/stock-ticker-events.js:
--------------------------------------------------------------------------------
1 | var server = connectToServer();
2 |
3 | var formatDecimal = unboundMethod( "toFixed" )( 2 );
4 | var formatPrice = pipe( formatDecimal, formatCurrency );
5 | var formatChange = pipe( formatDecimal, formatSign );
6 | var processNewStock = pipe( addStockName, formatStockNumbers );
7 | var observableMapperFns = [ processNewStock, formatStockNumbers ];
8 | var makeObservableFromEvent = curry( Rx.Observable.fromEvent, 2 )( server );
9 |
10 | var [ newStocks, stockUpdates ] = pipe(
11 | map( makeObservableFromEvent ),
12 | curry( zip )( observableMapperFns ),
13 | map( spreadArgs( transformObservable ) )
14 | )
15 | ( [ "stock", "stock-update" ] );
16 |
17 |
18 | // *********************
19 |
20 | function addStockName(stock) {
21 | return setProp( "name", stock, stock.id );
22 | }
23 |
24 | function formatStockNumbers(stock) {
25 | var updateTuples = [
26 | [ "price", formatPrice( stock.price ) ],
27 | [ "change", formatChange( stock.change ) ]
28 | ];
29 |
30 | return reduce( function formatter(stock,[propName,val]){
31 | return setProp( propName, stock, val );
32 | } )
33 | ( stock )
34 | ( updateTuples );
35 | }
36 |
37 | function formatSign(val) {
38 | if (Number(val) > 0) {
39 | return `+${val}`;
40 | }
41 | return val;
42 | }
43 |
44 | function formatCurrency(val) {
45 | return `$${val}`;
46 | }
47 |
48 | function transformObservable(mapperFn,obsv){
49 | return obsv.map( mapperFn );
50 | }
51 |
--------------------------------------------------------------------------------
/ch11-code/stock-ticker.css:
--------------------------------------------------------------------------------
1 | *, *:before, *:after {
2 | -moz-box-sizing: border-box;
3 | box-sizing: border-box;
4 | }
5 |
6 | #stock-ticker {
7 | background-color: #efefef;
8 | padding: 1em;
9 | margin: 0;
10 | width: 14em;
11 | }
12 |
13 | .stock {
14 | list-style-type: none;
15 | padding: 0;
16 | margin: 0 0 1em 0;
17 | }
18 |
19 | .stock:last-child {
20 | margin-bottom: 0;
21 | }
22 |
23 | .stock > span {
24 | display: inline-block;
25 | width: 4em;
26 | text-align: right;
27 | overflow: hidden;
28 | }
29 |
30 | .stock-name {
31 | font-weight: bold;
32 | text-align: center;
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/ch11-code/stock-ticker.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var stockTickerUI = {
4 |
5 | updateStockElems(stockInfoChildElemList,data) {
6 | var getDataVal = curry( reverseArgs( prop ), 2 )( data );
7 | var extractInfoChildElemVal = pipe(
8 | getClassName,
9 | stripPrefix( /\bstock-/i ),
10 | getDataVal
11 | );
12 | var orderedDataVals =
13 | map( extractInfoChildElemVal )( stockInfoChildElemList );
14 | var elemsValsTuples =
15 | filterOut( function updateValueMissing([infoChildElem,val]){
16 | return val === undefined;
17 | } )
18 | ( zip( stockInfoChildElemList, orderedDataVals ) );
19 |
20 | // !!SIDE EFFECTS!!
21 | compose( each, spreadArgs )( setDOMContent )
22 | ( elemsValsTuples );
23 | },
24 |
25 | updateStock(tickerElem,data) {
26 | var getStockElemFromId = curry( getStockElem )( tickerElem );
27 | var stockInfoChildElemList = pipe(
28 | getStockElemFromId,
29 | getStockInfoChildElems
30 | )
31 | ( data.id );
32 |
33 | return stockTickerUI.updateStockElems(
34 | stockInfoChildElemList,
35 | data
36 | );
37 | },
38 |
39 | addStock(tickerElem,data) {
40 | var [stockElem, ...infoChildElems] = map(
41 | createElement
42 | )
43 | ( [ "li", "span", "span", "span" ] );
44 | var attrValTuples = [
45 | [ ["class","stock"], ["data-stock-id",data.id] ],
46 | [ ["class","stock-name"] ],
47 | [ ["class","stock-price"] ],
48 | [ ["class","stock-change"] ]
49 | ];
50 | var elemsAttrsTuples =
51 | zip( [stockElem, ...infoChildElems], attrValTuples );
52 |
53 | // !!SIDE EFFECTS!!
54 | each( function setElemAttrs([elem,attrValTupleList]){
55 | each(
56 | spreadArgs( partial( setElemAttr, elem ) )
57 | )
58 | ( attrValTupleList );
59 | } )
60 | ( elemsAttrsTuples );
61 |
62 | // !!SIDE EFFECTS!!
63 | stockTickerUI.updateStockElems( infoChildElems, data );
64 | reduce( appendDOMChild )( stockElem )( infoChildElems );
65 | tickerElem.appendChild( stockElem );
66 | }
67 |
68 | };
69 |
70 | var getDOMChildren = pipe(
71 | listify,
72 | flatMap(
73 | pipe(
74 | curry( prop )( "childNodes" ),
75 | Array.from
76 | )
77 | )
78 | );
79 | var createElement = document.createElement.bind( document );
80 | var getElemAttrByName = curry( reverseArgs( getElemAttr ), 2 );
81 | var getStockId = getElemAttrByName( "data-stock-id" );
82 | var getClassName = getElemAttrByName( "class" );
83 | var ticker = document.getElementById( "stock-ticker" );
84 | var stockTickerUIMethodsWithDOMContext = map(
85 | curry( reverseArgs( partial ), 2 )( ticker )
86 | )
87 | ( [ stockTickerUI.addStock, stockTickerUI.updateStock ] );
88 | var subscribeToObservable =
89 | pipe( uncurry, spreadArgs )( unboundMethod( "subscribe" ) );
90 | var stockTickerObservables = [ newStocks, stockUpdates ];
91 |
92 | // !!SIDE EFFECTS!!
93 | each( subscribeToObservable )
94 | ( zip( stockTickerUIMethodsWithDOMContext, stockTickerObservables ) );
95 |
96 |
97 | // *********************
98 |
99 | function stripPrefix(prefixRegex) {
100 | return function mapperFn(val) {
101 | return val.replace( prefixRegex, "" );
102 | };
103 | }
104 |
105 | function listify(listOrItem) {
106 | if (!Array.isArray( listOrItem )) {
107 | return [ listOrItem ];
108 | }
109 | return listOrItem;
110 | }
111 |
112 | function isTextNode(node) {
113 | return node && node.nodeType == 3;
114 | }
115 |
116 | function getElemAttr(elem,prop) {
117 | return elem.getAttribute( prop );
118 | }
119 |
120 | function setElemAttr(elem,prop,val) {
121 | // !!SIDE EFFECTS!!
122 | return elem.setAttribute( prop, val );
123 | }
124 |
125 | function matchingStockId(id) {
126 | return function isStock(node){
127 | return getStockId( node ) == id;
128 | };
129 | }
130 |
131 | function isStockInfoChildElem(elem) {
132 | return /\bstock-/i.test( getClassName( elem ) );
133 | }
134 |
135 | function getStockElem(tickerElem,stockId) {
136 | return pipe(
137 | getDOMChildren,
138 | filterOut( isTextNode ),
139 | filterIn( matchingStockId( stockId ) )
140 | )
141 | ( tickerElem );
142 | }
143 |
144 | function getStockInfoChildElems(stockElem) {
145 | return pipe(
146 | getDOMChildren,
147 | filterOut( isTextNode ),
148 | filterIn( isStockInfoChildElem )
149 | )
150 | ( stockElem );
151 | }
152 |
153 | function appendDOMChild(parentNode,childNode) {
154 | // !!SIDE EFFECTS!!
155 | parentNode.appendChild( childNode );
156 | return parentNode;
157 | }
158 |
159 | function setDOMContent(elem,html) {
160 | // !!SIDE EFFECTS!!
161 | elem.innerHTML = html;
162 | return elem;
163 | }
164 |
--------------------------------------------------------------------------------
/fig1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig1.png
--------------------------------------------------------------------------------
/fig10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig10.png
--------------------------------------------------------------------------------
/fig10.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig11.png
--------------------------------------------------------------------------------
/fig11.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig12.png
--------------------------------------------------------------------------------
/fig12.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig13.png
--------------------------------------------------------------------------------
/fig13.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig14.png
--------------------------------------------------------------------------------
/fig15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig15.png
--------------------------------------------------------------------------------
/fig15.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig16.png
--------------------------------------------------------------------------------
/fig16.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig17.png
--------------------------------------------------------------------------------
/fig17.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig18.png
--------------------------------------------------------------------------------
/fig18.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig2.png
--------------------------------------------------------------------------------
/fig3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig3.png
--------------------------------------------------------------------------------
/fig3.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig4.png
--------------------------------------------------------------------------------
/fig4.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig5.png
--------------------------------------------------------------------------------
/fig5.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig6.png
--------------------------------------------------------------------------------
/fig6.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig7.png
--------------------------------------------------------------------------------
/fig8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig8.png
--------------------------------------------------------------------------------
/fig8.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fig9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrBoolean/Functional-Light-JS/51e6508ad8f808d39bbd6de16882bb0dad9221a3/fig9.png
--------------------------------------------------------------------------------
/fig9.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/foreword.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript
2 | # Foreword
3 |
4 | It's no secret that I am a functional programming nut. I evangelize functional ideas and languages wherever I can, try to read the latest academic papers, study abstract algebra in my spare time…the works. Even in JavaScript, I refuse to write an impure statement, which is what lead to writing Professor Frisby's Mostly Adequate Guide to Functional Programming. Yep, full on, dogmatic zealot.
5 |
6 | I was not always this way… I was once obsessed with objects. I loved modeling the "real world". I was the inventor of synthetic automatons, tinkering through the night with masterful precision. The creator of sentient puppets, fingers dancing on the keyboard to give them life - a real 1337 h4x0r Geppetto. Yet, after 5 SOLID years of writing object oriented code, I was never quite satisfied with the outcome. It just never worked out well for me. I felt like a lousy programmer. I even lost faith that a simple, flexible codebase of decent scale was possible.
7 |
8 | I figured I'd try something different: Functional programming. I began to dabble with functional ideas in my every day codebase and, much to my coworkers dismay, hadn't the slightest clue what I was doing. The code I wrote in those days was awful. Atrocious. Digital sewage. The reason was a lack of clear vision or goal on what I was even trying to accomplish. My Jiminy-Coding-Cricket, if you like, was not there to guide me. It took a long time and a lot of garbage programs to figure out how to FP.
9 |
10 | Now, after all that messy exploration, I feel that pure functional programming has delivered on its promise. Readable programs do exist! Reuse does exist! I no longer invent, but rather discover my model. I've become a rogue detective uncovering a vast conspiracy, cork board pinned full of mathematical evidence. A digital-age Cousteau logging the characteristics of this bizarre land in the name of science! It's not perfect and I still have a lot to learn, but I've never been more satisfied in my work and pleased with the outcome.
11 |
12 | Had this book existed when I was starting out, my transition into the world of functional programming would have been much easier and less destructive. This book is two fold (right and left): it will not only teach you how to use various constructs from FP effectively in your daily code, but more importantly, provide you with an aim; guiding principles that will keep you on track.
13 |
14 | You will learn Functional-Lite: A paradigm that Kyle has pioneered to enable declarative, functional programming while providing balance and interop with the rest of the JavaScript world. You will understand the foundation which pure FP is built upon without having to subscribe to the paradigm in its entirety. You will gain the skills to practice and explore FP without having to re-write existing code for it to work well together. You can take a step forward in your software career without backtracking and wandering aimlessly as I did years ago. Coworkers and colleagues rejoice!
15 |
16 | Kyle is a great teacher known for his relentless pursuit of the whole picture, leaving no nook or cranny unexplored yet he maintains an empathy for the learners plight. His style has resonated with the industry, leveling us all up as a whole. His work has a solid place in JavaScript’s history and most people's bookmark bar. You are in good hands.
17 |
18 | Functional programming has many different definitions. A Lisp programmer's definition is vastly different from a Haskell perspective. OCaml's FP bears little resemblance to the paradigm seen in Erlang. You will even find several competing definitions in JavaScript. Yet there is a tie that binds - some blurry know-it-when-I-see-it definition, much like obscenity (indeed, some do find FP obscene!) and this book certainly captures it. The end result might not be considered idiomatic in certain circles, but the knowledge acquired here directly applies to any flavor of FP.
19 |
20 | This book is a terrific place to begin your FP journey. Take it away Kyle...
21 |
22 | *-Brian Lonsdorf (@drboolean)*
23 |
--------------------------------------------------------------------------------
/preface.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript
2 | # Preface
3 |
4 | > A monad is just a monoid in the category of endofunctors.
5 |
6 | Did I just lose you? Don't worry, I'd be lost, too! All those terms that only mean something to the already-initiated in Functional Programming™ (FP) are just jumbled nonsense to many of the rest of us.
7 |
8 | This book is not going to teach you what those words mean. If that's what you're looking for, keep looking. In fact, there's already plenty of great books that teach FP the *right way*, from the top-down. Those words have important meanings and if you formally study FP in-depth, you'll absolutely want to get familiar with them.
9 |
10 | But this book is going to approach the topic quite differently. I'm going to present fundamental FP concepts from the ground-up, with fewer special or non-intuitive terms than most approaches to FP. We'll try to take a practical approach to each principle rather than a purely academic angle. **There will be terms**, no doubt. But we'll be careful and deliberate about introducing them and explaining why they're important.
11 |
12 | Sadly, I am not a card-carrying member of the FP cool kids club. I've never been formally taught anything about FP. And though I have a CS academic background and I was decent at math, mathematical notation is not how my brain understands programming. I have never written a line of Scheme, Clojure, or Haskell. I'm not an old-school Lisp'r.
13 |
14 | I *have* attended countless conference talks about FP, each one with the desperate clinging hope that finally, *this time*, would be the time I understood what this whole functional programming mysticism is all about. And each time, I came away frustrated and reminded that those terms got all mixed up in my head and I had no idea if or what I learned. Maybe I learned things. But I couldn't figure out what those things were, for the longest time.
15 |
16 | Little by little, across those various exposures, I teased out bits and pieces of important concepts that seem to just come all too naturally to the formal FPer. I learned them slowly and I learned them pragmatically and experientially, not academically with appropriate terminology. Have you ever known a thing for a long time, and only later found out it had a specific name you never knew!?
17 |
18 | Maybe you're like me; I heard terms like "map-reduce" around industry segments like "big data" for years with no real idea what they were. Eventually I learned what the `map(..)` function did -- all long before I had any idea that list operations were a cornerstone of the FPer path and what makes them so important. I knew what *map* was long before I ever knew it was called `map(..)`.
19 |
20 | Eventually I began to gather all these tidbits of understanding into what I now call "Functional-Light Programming" (FLP).
21 |
22 | ## Mission
23 |
24 | But, why is it so important for you to learn functional programming, even the light form?
25 |
26 | I've come to believe something very deeply in recent years, so much so you could *almost* call it a religious belief. I believe that programming is fundamentally about humans, not about code. I believe that code is first and foremost a means of human communication, and only as a *side effect* (hear my self-referential chuckle) does it instruct the computer.
27 |
28 | The way I see it, functional programming is at its heart about using patterns in your code that are well-known, understandable, *and* proven to keep away the mistakes that make code harder to understand. In that view, FP -- or, ahem, FLP! -- might be one of the most important collections of tools any developer could acquire.
29 |
30 | > The curse of the monad is that... once you understand... you lose the ability to explain it to anyone else.
31 | >
32 | > Douglas Crockford 2012 "Monads and Gonads"
33 | >
34 | > https://www.youtube.com/watch?v=dkZFtimgAcM
35 |
36 | I hope this book "Maybe" breaks the spirit of that curse, even though we won't talk about "monads" until the very end in the appendices.
37 |
38 | The formal FPer will often assert that the *real value* of FP is in using it essentially 100%: it's an all-or-nothing proposition. The belief is that if you use FP in one part of your program but not in another, the whole program is polluted by the non-FP stuff and therefore suffers enough that the FP was probably not worth it.
39 |
40 | I'll say unequivocally: **I think that absolutism is bogus**. That's as silly to me as suggesting that this book is only good if I use perfect grammar and active voice throughout; if I make any mistakes, it degrades the entire book's quality. Nonsense.
41 |
42 | The better I am at writing in a clear, consistent voice, the better this book experience will be for you. But I'm not a 100% perfect author. Some parts will be better written than others. The parts where I can still improve are not going to invalidate the other parts of this book which are useful.
43 |
44 | And so it goes with our code. The more you can apply these principles to more parts of your code, the better your code will be. Use them well 25% of the time, and you'll get some good benefit. Use them 80% of the time, and you'll see even more benefit.
45 |
46 | With perhaps a few exceptions, I don't think you'll find many absolutes in this text. We'll instead talk about aspirations, goals, principles to strive for. We'll talk about balance and pragmatism and trade-offs.
47 |
48 | Welcome to this journey into the most useful and practical foundations of FP. We both have plenty to learn!
49 |
--------------------------------------------------------------------------------
/toc.md:
--------------------------------------------------------------------------------
1 | # Functional-Light JavaScript
2 |
3 | ## Table of Contents
4 |
5 | * Foreword
6 | * Preface
7 | * Chapter 1: Why Functional Programming?
8 | * Confidence
9 | * Communication
10 | * Take
11 | * YAGNI
12 | * Resources
13 | * Chapter 2: Foundations of Functional Functions
14 | * What Is A Function?
15 | * Function Input
16 | * Function Output
17 | * Functions Of Functions
18 | * Syntax
19 | * What's This?
20 | * Chapter 3: Managing Function Inputs
21 | * Some Now, Some Later
22 | * One At A Time
23 | * All For One
24 | * Spread 'Em Out
25 | * Order Matters
26 | * No Points
27 | * Chapter 4: Composing Functions
28 | * Output To Input
29 | * Reordered Composition
30 | * Abstraction
31 | * Revisiting Points
32 | * Chapter 5: Reducing Side Effects
33 | * Effects On The Side, Please
34 | * Once Is Enough, Thanks
35 | * Pure Bliss
36 | * There Or Not
37 | * Purifying
38 | * Chapter 6: Value Immutability
39 | * Primitive Immutability
40 | * Value To Value
41 | * Reassignment
42 | * Performance
43 | * Treatment
44 | * Chapter 7: Closure vs Object
45 | * The Same Page
46 | * Look Alike
47 | * Two Roads Diverged In A Wood...
48 | * Chapter 8: List Operations
49 | * Non-FP List Processing
50 | * Map
51 | * Filter
52 | * Reduce
53 | * Advanced List Operations
54 | * Method vs. Standalone
55 | * Looking For Lists
56 | * Fusion
57 | * Beyond Lists
58 | * Chapter 9: Recursion
59 | * Definition
60 | * Declarative Recursion
61 | * Stack
62 | * Rearranging Recursion
63 | * Chapter 10: Functional Async
64 | * Time As State
65 | * Eager vs Lazy
66 | * Reactive FP
67 | * Chapter 11: Putting It All Together
68 | * Setup
69 | * Stock Events
70 | * Stock Ticker UI
71 | * Appendix A: Transducing
72 | * Appendix B: The Humble Monad
73 | * Appendix C: FP Libraries
74 |
--------------------------------------------------------------------------------