29,99 €
This book delves into functional programming and composition techniques in JavaScript, starting with core concepts like pure functions, shared state avoidance, and higher-order functions to build modular, maintainable code. Early chapters explore the fundamentals of functional programming, immutability, and its growing influence in the JavaScript community. You'll learn essential topics such as function composition, currying, and higher-order functions, as well as advanced concepts like abstract data types, functors, and monads. The book discusses the evolution of functional programming, its role in modern software development, and addresses challenges like the software crisis and composing with classes.
You'll learn essential topics like object-oriented programming, focusing on factory functions, functional mixins, and object composition. You'll also understand why traditional classes complicate composition and discover strategies for creating custom data types, lenses, and transducers. The book also covers best practices, emphasizing clean, reusable code and avoiding anti-patterns like excessive mocking.
By the end, you'll be ready to apply functional programming techniques to tackle complex design challenges and write more maintainable JavaScript code.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 348
Veröffentlichungsjahr: 2024
This version was published on 2024-08-11
* * * * *
Thank You
Composing Software: An Introduction
You Compose Software Every Day
Conclusion
The Dao of Immutability (The Way of the Functional Programmer)
Forward
The Rise and Fall and Rise of Functional Programming (Composable Software)
The Rise of Functional Programming
The Fall of Functional Programming
The Rise of Functional Programming
Functional Programming Has Always Been Alive and Well
Why Learn Functional Programming in JavaScript?
Pure Functions
What is a Function?
Mapping
Pure Functions
The Trouble with Shared State
Same Input, Same Output
No Side Effects
Conclusion
What is Functional Programming?
Pure Functions
Function Composition
Shared State
Immutability
Side Effects
Reusability Through Higher Order Functions
Containers, Functors, Lists, and Streams
Declarative vs Imperative
Conclusion
A Functional Programmer’s Introduction to JavaScript
Expressions and Values
Types
Destructuring
Comparisons and Ternaries
Functions
Currying
Function Composition
Arrays
Method Chaining
Conclusion
Higher Order Functions
Curry and Function Composition
What is a curried function?
What is a partial application?
What’s the Difference?
What is point-free style?
Why do we curry?
Trace
Curry and Function Composition, Together
Conclusion
Abstraction & Composition
Abstraction is simplification.
Abstraction in Software
Abstraction through composition
How to Do More with Less Code
Conclusion
Reduce
Reduce is Versatile
Conclusion
Abstract Data Types and the Software Crisis
Common ADT Examples
Why ADTs?
History of ADTs
Specifications for ADTs
Stack ADT Example
Concrete Implementations
Conclusion
Glossary
Functors & Categories
Why Functors?
Functor Laws
Category Theory
Build Your Own Functor
Curried Map
Conclusion
Monads
You’re probably already using monads.
What Monads are Made of
Building a Kleisli Composition Function
The Monad Laws
Conclusion
The Forgotten History of OOP
The Big Idea
The Essence of OOP
What OOP Doesn’t Mean
What is an object?
We’ve lost the plot.
Object Composition
What is Object Composition?
Three Different Forms of Object Composition
Notes on Code Examples
Aggregation
Concatenation
Delegation
Conclusion
Factory Functions
Literals for One, Factories for Many
Returning Objects
Destructuring
Computed Property Keys
Default Parameters
Type Inference
Factory Functions for Mixin Composition
Conclusion
Functional Mixins
Motivation
What are mixins?
What is functional inheritance?
What is a functional mixin?
Composing Functional Mixins
When to Use Functional Mixins
Caveats
Conclusion
Why Composition is Harder with Classes
The Delegate Prototype
The
.constructor
Property
Class to Factory is a Breaking Change
Composable Custom Data Types
You can do this with any data type
Composable Currency
Lenses
Why Lenses?
Background
Lens Laws
Composing Lenses
Transducers
Why Transducers?
Background and Etymology
A Musical Analogy for Transducers
Transducers compose top-to-bottom.
Transducer Rules
Transducing
The Transducer Protocol
Conclusion
Elements of JavaScript Style
1. Make the function the unit of composition. One job for each function.
2. Omit needless code.
3. Use active voice.
4. Avoid a succession of loose statements.
5. Keep related code together.
6. Put statements and expressions in positive form.
7. Use parallel code for parallel concepts.
Conclusion: Code should be simple, not simplistic.
Mocking is a Code Smell
TDD should lead to better design.
What is a code smell?
What is a mock?
What is a unit test?
What is test coverage?
What is tight coupling?
What causes tight coupling?
What does composition have to do with mocking?
How do we remove coupling?
“Code smells” are warning signs, not laws. Mocks are not evil.
Begin Reading
To my editor, JS Cheerleader, who made this book much better in too many ways to list. If you find the text readable, it is because she carefully pored over every page and offered insightful feedback and encouragement every step of the way. Without her help, you would not be reading this book right now.
To the blog readers, whose support and enthusiasm helped us turn a little blog post series into a phenomenon that attracted millions of readers and provided the momentum we needed to turn it into a book.
To the legends of computer science who paved the way.
“If I have seen further it is by standing on the shoulders of Giants.” ~ Sir Isaac Newton
Functional programming is a foundational pillar of JavaScript, and immutability is a foundational pillar of functional programming. You can’t fully understand functional programming without first understanding immutability. This story may help.
I was wandering the archives of an old library, and found a dark tunnel that led to the chamber of computation. There I found a scroll that seemed to have fallen on the floor, forgotten.
The scroll was encased in a dusty steel tube and labeled: “From the archives of The Church of Lambda.”
It was wrapped in a thin sheet of paper that read:
A master programmer and his apprentice sat in Turing meditation, contemplating the Lambda. The apprentice looked at the master and asked, “Master, you tell me to simplify, but programs are complex. Frameworks ease the work of creation by removing hard choices. Which is better, a class or a framework?” The master programmer looked at his apprentice and asked, “did you not read the teachings of wise master Carmack?”, quoting…
“Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function.”
“But master,” the apprentice started — but the master interrupted, asking:
“Is it not true that the word for ‘not functional’ is ‘dysfunctional’?”
And then the apprentice understood.
On the back of the sheet was an index that seemed to refer to many books in the chamber of computation. I peeked inside the books, but they were filled with big words I did not understand, so I put them back and continued to read the scroll. I noticed that the margins of the index were filled with short commentary, which I will reproduce here, faithfully, along with the writing from the scroll:
Immutability: The true constant is change. Mutation hides change. Hidden change manifests chaos. Therefore, the wise embrace history.
If you have a dollar, and I give you another dollar, it does not change the fact that a moment ago you only had one dollar, and now you have two. Mutation attempts to erase history, but history cannot be truly erased. When the past is not preserved, it will come back to haunt you, manifesting as bugs in the program.
Separation: Logic is thought. Effects are action. Therefore the wise think before acting, and act only when the thinking is done.
If you try to perform effects and logic at the same time, you may create hidden side effects which cause bugs in the logic. Keep functions small. Do one thing at a time, and do it well.
Composition: All things in nature work in harmony. A tree cannot grow without water. A bird cannot fly without air. Therefore the wise mix ingredients together to make their soup taste better.
Plan for composition. Write functions whose outputs will naturally work as inputs to many other functions. Keep function signatures as simple as possible. Conservation: Time is precious, and effort takes time. Therefore the wise reuse their tools as much as they can. The foolish create special tools for each new creation.
Type-specific functions can’t be reused for data of a different type. Wise programmers lift functions to work on many data types, or wrap data to make it look like what the function is expecting. Lists and items make great universal abstractions.
Flow: still waters are unsafe to drink. Food left alone will rot. The wise prosper because they let things flow.
[Editor’s note: The only illustration on the scroll was a row of different-looking ducks floating down a stream just above this verse. I have not reproduced the illustration because I don’t believe I could do it justice. Curiously, it was captioned:]
A list expressed over time is a stream.
[The corresponding note read:]
Shared objects and data fixtures can cause functions to interfere with each other. Threads competing for the same resources can trip each other up. A program can be reasoned about and outcomes predicted only when data flows freely through pure functions.
[Editor’s note: The scroll ended with this final verse, which had no commentary:]
Wisdom: The wise programmer understands the way before walking the path. Therefore the wise programmer is functional, while the unwise get lost in the jungle.
Forget whatever you think you know about JavaScript, and approach this material with a beginner’s mind. To help you do that, we’re going to review the JavaScript basics from the ground up, as if you’ve never seen JavaScript before. If you’re a beginner, you’re in luck. Finally something exploring ES6 and functional programming from scratch! Hopefully all the new concepts are explained along the way – but don’t count on too much pampering.
If you’re a seasoned developer already familiar with JavaScript, or a pure functional language, maybe you’re thinking that JavaScript is a funny choice for an exploration of functional programming. Set those thoughts aside, and try to approach the material with an open mind. You may find that there is another level to JavaScript programming. One you never knew existed.
Since this text is called “Composing Software”, and functional programming is the obvious way to compose software (using function composition, higher order functions, etc.), you may be wondering why I’m not talking about Haskell, ClojureScript, or Elm, instead of JavaScript.
JavaScript has the most important features needed for functional programming:
First class functions: The ability to use functions as data values: pass functions as arguments, return functions, and assign functions to variables and object properties. This property allows for higher order functions, which enable partial application, currying, and composition.Anonymous functions and concise lambda syntax:x => x * 2 is a valid function expression in JavaScript. Concise lambdas make it easier to work with higher-order functions.Closures: A closure is the bundling of a function with its lexical environment. Closures are created at function creation time. When a function is defined inside another function, it has access to the variable bindings in the outer function, even after the outer function exits. Closures are how partial applications get their fixed arguments. A fixed argument is an argument bound in the closure scope of a returned function. In add2(1)(2), 1 is a fixed argument in the function returned by add2(1).JavaScript is a multi-paradigm language, meaning that it supports programming in many different styles. Other styles supported by JavaScript include procedural (imperative) programming (like C), where functions represent a subroutine of instructions that can be called repeatedly for reuse and organization, object-oriented programming, where objects – not functions – are the primary building blocks, and of course, functional programming. The disadvantage of a multi-paradigm language is that imperative and object-oriented programming tend to imply that almost everything needs to be mutable.
Mutation is a change to data structure that happens in-place. For example:
Objects usually need to be mutable so that their properties can be updated by methods. In imperative programming, most data structures are mutable to enable efficient in-place manipulation of objects and arrays.
Here are some features that some functional languages have, that JavaScript does not have:
Purity: In some FP languages, purity is enforced by the language. Expressions with side-effects are not allowed.Immutability: Some FP languages disable mutations. Instead of mutating an existing data structure, such as an array or object, expressions evaluate to new data structures. This may sound inefficient, but most functional languages use trie data structures under the hood, which feature structural sharing: meaning that the old object and new object share references to the data that is the same.Recursion: Recursion is the ability for a function to reference itself for the purpose of iteration. In many FP languages, recursion is the only way to iterate. There are no loop statements like for, while, or do loops.Purity: In JavaScript, purity must be achieved by convention. If you’re not building most of your application by composing pure functions, you’re not programming using the functional style. It’s unfortunately easy in JavaScript to get off track by accidentally creating and using impure functions.
Immutability: In pure functional languages, immutability is often enforced. JavaScript lacks efficient, immutable trie-based data structures used by most functional languages, but there are libraries that help, including Immutable.js and Mori. I’m hoping that future versions of the ECMAScript spec will embrace immutable data structures. There are signs that offer hope, like the addition of the const keyword in ES6. A name binding defined with const can’t be reassigned to refer to a different value. It’s important to understand that const does not represent an immutable value.
A const object can’t be reassigned to refer to a completely different object, but the object it refers to can have its properties mutated. JavaScript also has the ability to freeze() objects, but those objects are only frozen at the root level, meaning that a nested object can still have properties of its properties mutated. In other words, there’s still a long road ahead before we see true composite immutables in the JavaScript specification.
Recursion: JavaScript technically supports recursion, but most functional languages have a feature called proper tail calls. Proper tail calls are a language feature which allows recursive functions to reuse stack frames for recursive calls.
Without proper tail calls, a call stack can grow without bounds and cause a stack overflow. JavaScript technically got proper tail calls in the ES6 specification. Unfortunately, only one of the major browser engines enabled it as a default feature, and the optimization was partially implemented and then subsequently removed from Babel (the most popular standard JavaScript compiler, used to compile ES6 to ES5 for use in older browsers).
Bottom line: It still isn’t safe to use recursion for large iterations — even if you’re careful to call the function in the tail position.
A purist will tell you that JavaScript’s mutability is its major disadvantage, which is sometimes true. However, side effects and mutation are sometimes beneficial. In fact, it’s impossible to create most useful modern applications without effects. Pure functional languages like Haskell use effects, but camouflage them from pure functions using boxes called monads, allowing the program to remain pure even though the effects represented by the monads are impure.