Composing Software - Eric Elliott - E-Book

Composing Software E-Book

Eric Elliott

0,0
29,99 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

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:

EPUB
MOBI

Seitenzahl: 348

Veröffentlichungsjahr: 2024

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Composing Software

An Exploration of Functional Programming and Object Composition in JavaScript

 

Eric Elliott

 

This version was published on 2024-08-11

*   *   *   *   *

© 2017 - 2024 Eric Elliott

Table of Contents

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.

Guide

Begin Reading

Thank You

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

The Dao of Immutability (The Way of the Functional Programmer)

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.

Forward

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.

Why Learn Functional Programming in JavaScript?

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).

What JavaScript is Missing

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:

1 constfoo{2 bar:'baz'3 };4 5 foo.bar'qux';// mutation

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.

What JavaScript Has that Pure Functional Languages Lack

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.