Grokking Functional Programming

This document was uploaded by one of our users. The uploader already confirmed that they had the permission to publish it. If you are author/publisher or own the copyright of this documents, please report to us by using this DMCA report form.

Simply click on the Download Book button.

Yes, Book downloads on Ebookily are 100% Free.

Sometimes the book is free on Amazon As well, so go ahead and hit "Search on Amazon"

There’s no need to fear going functional! This friendly, lively, and engaging guide is perfect for any perplexed programmer. It lays out the principles of functional programming in a simple and concise way that will help you grok what FP is really all about. In Grokking Functional Programming you will learn: • Designing with functions and types instead of objects • Programming with pure functions and immutable values • Writing concurrent programs using the functional style • Testing functional programs • Multiple learning approaches to help you grok each new concept If you’ve ever found yourself rolling your eyes at functional programming, this is the book for you. Open up Grokking Functional Programming and you’ll find functional ideas mapped onto what you already know as an object-oriented programmer. The book focuses on practical aspects from page one. Hands-on examples apply functional principles to everyday programming tasks like concurrency, error handling, and improving readability. Plus, puzzles and exercises let you think and practice what you're learning. You’ll soon reach an amazing “aha” moment and start seeing code in a completely new way. About the technology Finally, there’s an easy way to learn functional programming! This unique book starts with the familiar ideas of OOP and introduces FP step-by-step using relevant examples, engaging exercises, and lots of illustrations. You’ll be amazed at how quickly you’ll start seeing software tasks from this valuable new perspective. About the book Grokking Functional Programming introduces functional programming to imperative developers. You’ll start with small, comfortable coding tasks that expose basic concepts like writing pure functions and working with immutable data. Along the way, you’ll learn how to write code that eliminates common bugs caused by complex distributed state. You’ll also explore the FP approach to IO, concurrency, and data streaming. By the time you finish, you’ll be writing clean functional code that’s easy to understand, test, and maintain. What's inside • Designing with functions and types instead of objects • Programming with pure functions and immutable values • Writing concurrent programs using the functional style • Testing functional programs About the reader For developers who know an object-oriented language. Examples in Java and Scala. About the author Michal Plachta is an experienced software developer who regularly speaks and writes about creating maintainable applications.

Author(s): Michał Płachta
Edition: 1
Publisher: Manning Publications
Year: 2022

Language: English
Commentary: Vector PDF
Pages: 520
City: Shelter Island, NY
Tags: Multithreading; Concurrency; Functional Programming; Stream Processing; Scala; Error Handling; Testing; Pure Functions

Functional Programming
contents
preface
acknowledgments
about this book
about the authors
Part 1 The functional toolkit
1 Learning functional programming
Perhaps you picked up this book because...
What do you need to know before we start?
What do functions look like?
Meet the function
When the code lies...
Imperative vs. declarative
Coffee break: Imperative vs. declarative
Coffee break explained: Imperative vs. declarative
How useful is learning functional programming?
Leaping into Scala
Practicing functions in Scala
Getting your tools ready
Getting to know the REPL
Writing your first functions!
How to use this book
2 Pure functions
Why do we need pure functions?
Coding imperatively
Breaking the code
Passing copies of the data
Breaking the code . . . again
Recalculating instead of storing
Focusing on the logic by passing the state
Where did the state go?
The difference between impure and pure functions
Coffee break: Refactoring to a pure function
Coffee break explained: Refactoring to a pure function
In pure functions we trust
Pure functions in programming languages
Difficulty of staying pure...
Pure functions and clean code
Coffee break: Pure or impure?
Coffee break explained: Pure or impure?
Using Scala to write pure functions
Practicing pure functions in Scala
Testing pure functions
Coffee break: Testing pure functions
Coffee break explained: Testing pure functions
3 Immutable values
The fuel for the engine
Another case for immutability
Can you trust this function?
Mutability is dangerous
Functions that lie... again
Fighting mutability by working with copies
Coffee break: Getting burned by mutability
Coffee break explained: Getting burned by mutability
Introducing shared mutable state
State’s impact on programming abilities
Dealing with the moving parts
Dealing with the moving parts using FP
Immutable values in Scala
Building our intuition about immutability
Coffee break: The immutable String API
Coffee break explained: The immutable String API
Hold on . . . Isn’t this bad?
Purely functional approach to shared mutable state
Practicing immutable slicing and appending
4 Functions as values
Implementing requirements as functions
Impure functions and mutable values strike back
Using Java Streams to sort the list
Function signatures should tell the whole story
Changing requirements
We just pass the code around!
Using Java’s Function values
Using the Function syntax to deal with code duplication
Passing user-defined functions as arguments
Coffee break: Functions as parameters
Coffee break explained: Functions as parameters
Problems with reading functional Java
Passing functions in Scala
Deep dive into sortBy
Signatures with function parameters in Scala
Passing functions as arguments in Scala
Practicing function passing
Embracing declarative programming
Passing functions to custom-made functions
Small functions and their responsibilities
Passing functions inline
Coffee break: Passing functions in Scala
Coffee break explained: Passing functions in Scala
What else can we achieve just by passing functions?
Applying a function to each element of a list
Applying a function to each element of a list using map
Getting to know map
Practicing map
Learn once, use everywhere
Returning parts of the list based on a condition
Returning parts of the list using filter
Getting to know filter
Practicing filter
Our journey so far...
Don’t repeat yourself?
Is my API easy to use?
Adding a new parameter is not enough
Functions can return functions
Using functions that can return functions
Functions are values
Coffee break: Returning functions
Coffee break explained: Returning functions
Designing functional APIs
Iterative design of functional APIs
Returning functions from returned functions
How to return functions from returned functions
Using the flexible API built with returned functions
Using multiple parameter lists in functions
We have been currying!
Practicing currying
Programming by passing function values
Reducing many values into a single value
Reducing many values into a single one using foldLeft
Getting to know foldLeft
foldLeft must-knows
Practicing foldLeft
Modeling immutable data
Using product types with higher-order functions
More concise syntax for inline functions
Part 2 Functional programs
5 Sequential programs
Writing pipeline-based algorithms
Composing larger programs from smaller pieces
The imperative approach
flatten and flatMap
Practical use case of using more flatMaps
flatMap and changing the size of the list
Coffee break: Dealing with lists of lists
Coffee break explained: Dealing with lists of lists
Chained flatMaps and maps
Nested flatMaps
Values that depend on other values
Practicing nested flatMaps
A better syntax for nested flatMaps
For comprehensions to the rescue!
Coffee break: flatMaps vs. for comprehensions
Coffee break explained: flatMaps vs. for comprehensions
Getting to know for comprehensions
It’s not the for you are looking for!
Inside a for comprehension
More sophisticated for comprehensions
Checking all combinations using a for comprehension
Filtering techniques
Coffee break: Filtering techniques
Coffee break explained: Filtering techniques
Looking for a greater abstraction
Comparing map, foldLeft, and flatMap
Using for comprehensions with Sets
Using for comprehensions with many types
Practicing for comprehensions
Defining for comprehensions... again
Using for comprehensions with noncollection types
Avoiding nulls: Option type
Parsing as a pipeline
Coffee break: Parsing with Option
Coffee break explained: Parsing with Option
6 Error handling
Handling lots of different errors, gracefully
Is it even possible to handle them all?
Sort the list of TV shows by their running time
Implementing the sorting requirement
Dealing with data coming from the outside world
Functional design: Building from small blocks
Parsing Strings into immutable objects
Parsing a List is just parsing one element
Parsing a String into a TvShow
What about potential errors?
Is returning null a good idea?
How do we handle potential errors more gracefully?
Implementing a function that returns an Option
Option forces us to handle possible errors
Building from small blocks
Functional design is building from small blocks
Writing a small, safe function that returns an Option
Functions, values, and expressions
Practicing safe functions that return Options
How do errors propagate?
Values represent errors
Option, for comprehensions, and checked exceptions...
What about checked exceptions?
Conditional recovery
Conditional recovery using the imperative style
Checked exceptions don’t compose—Options do!
How does orElse work?
Practicing functional error handling
Functions compose, even in the presence of errors
Compiler reminds us that errors need to be covered
Compilation errors are good for us!
Transforming a List of Options into a flat List
Let the compiler be our guide...
...but let’s not trust the compiler too much!
Coffee break: Error-handling strategies
Coffee break explained: Error-handling strategies
Two different error-handling strategies
All-or-nothing error-handling strategy
Folding a List of Options into an Option of a List
We now know how to handle multiple possible errors!
How to know what failed
We need to convey error details in the return value
Conveying error details using Either
Refactoring to Either
Returning an Either instead of an Option
Practicing safe functions that return Either
What we learned about Option works with Either
Coffee break: Error handling using Either
Coffee break explained: Error handling using Either
Working with Option/Either
7 Requirements as types
Modeling data to minimize programmers’ mistakes
Well-modeled data can’t lie
Designing using what we know so far (which is primitive types)
Using data modeled as primitive types
Coffee break: The pain of primitive types
Coffee break explained: The pain of primitive types
Problems with the primitive type approach to modeling
Using primitive types makes our jobs harder!
Newtypes protect against misplaced parameters
Using newtypes in data models
Practicing newtypes
Making sure only valid data combinations are possible
Modeling possibility of absence in your data
Changes in the model force changes in the logic
Using data modeled as Options in your logic
Higher-order functions for the win!
There is probably a higher-order function for that!
Coffee break: forall/exists/contains
Coffee break explained: forall/exists/contains
Coupling a concept inside a single product type
Modeling finite possibilities
Using sum types
Even better modeling with sum types
Using the sum type + product type combo
Product types + sum types = algebraic data types (ADTs)
Using ADT-based models in behaviors (functions)
Destructuring ADTs using pattern matching
Duplication and DRY
Practicing pattern matching
Newtypes, ADTs, and pattern matching in the wild
What about inheritance?
Coffee break: Functional data design
Coffee break explained: Functional data design
Modeling behaviors
Modeling behaviors as data
Implementing functions with ADT-based parameters
Coffee break: Design and maintainability
Coffee break explained: Design and maintainability
8 IO as values
Talking to the outside world
Integrating with an external API
Properties of a side-effectful IO action
Imperative solution to side-effecting IO code
Problems with the imperative approach to IO
Can we really do better using FP?
Doing IO vs. using IO’s result
Handling IO imperatively
Computations as IO values
IO values
IO values in the wild
Pushing the impurity out
Using values fetched from two IO actions
Combining two IO values into a single IO value
Practicing creating and combining IO values
Disentangling concerns by working with values only
The IO type is viral
Coffee break: Working with values
Coffee break explained: Working with values
Toward functional IO
What about IO failures?
Running a program described by IO may fail!
Remember orElse?
Lazy and eager evaluation
Implementing recovery strategies using IO.orElse
Implementing fallbacks using orElse and pure
Practicing failure recovery in IO values
Where should we handle potential failures?
Toward functional IO with failure handling
Pure functions don’t lie, even in the unsafe world!
Functional architecture
Using IO to store data
Coffee break: Using IO to store data
Coffee break explained: Using IO to store data
Treating everything as values
Treating retries as values
Treating an unknown number of API calls as values
Practicing functional signature intuitions
9 Streams as values
To infinity and beyond
Dealing with an unknown number of values
Dealing with external impure API calls (again)
The functional approach to the design
Immutable maps
Practicing immutable maps
How many IO calls should we make?
The bottom-up design
Advanced list operations
Introducing tuples
Zipping and dropping
Pattern matching on tuples
Coffee break: Working with maps and tuples
Coffee break explained: Working with maps and tuples
Functional jigsaw puzzle
Following types in a bottom-up design
Prototyping and dead ends
Recursive functions
Infinity and laziness
Recursive function structure
Dealing with an absence in the future (using recursion)
Usefulness of infinite recursive calls
Coffee break: Recursion and infinity
Coffee break explained: Recursion and infinity
Creating different IO programs using recursion
Using recursion to make an arbitrary number of calls
Problems with the recursive version
Introducing data streams
Streams in imperative languages
Values on demand
Stream processing, producers, and consumers
Streams and IO
The functional Stream
Streams in FP are values
Streams are recursive values
Primitive operations and combinators
Streams of IO-based values
Infinite streams of IO-based values
Executing for side effects
Practicing stream operations
Using streams to our advantage
Infinite stream of API calls
Handling IO failures in streams
Separated concerns
Sliding windows
Waiting between IO calls
Zipping streams
Benefits of using the stream-based approach
10 Concurrent programs
Threads, threads everywhere
Declarative concurrency
Sequential vs. concurrent
Coffee break: Sequential thinking
Coffee break explained: Sequential thinking
The need for batching
Batching implementation
The concurrent world
The concurrent state
Imperative concurrency
Atomic references
Introducing Ref
Updating Ref values
Using Ref values
Making it all concurrent
parSequence in action
Practicing concurrent IOs
Modeling concurrency
Coding using Refs and fibers
IOs that run infinitely
Coffee break: Concurrent thinking
Coffee break explained: Concurrent thinking
The need for asynchronicity
Preparing for asynchronous access
Designing functional asynchronous programs
Managing fibers manually
Coding functional asynchronous programs
Part 3 Applied functional programming
11 Designing functional programs
Make it work, make it right, make it fast
Modeling using immutable values
Business domain modeling and FP
Data access modeling
A bag of functions
Business logic as a pure function
Separating the real data access concern
Integrating with APIs using imperative libraries and IO
Following the design
Implementing input actions as IO values
Separating the library IO from other concerns
Currying and inversion of control
Functions as values
Connecting the dots
We made it work
Making it right
Resource leaks
Handling resources
Using a Resource value
We made it right
Coffee break: Make it fast
Coffee break explained: Make it fast
12 Testing functional programs
Do you have tests for that?
Tests are just functions
Choosing functions to test
Testing by providing examples
Practicing testing by example
Generating good examples
Generating properties
Property-based testing
Testing by providing properties
Delegating the work by passing functions
Understanding failures of property-based tests
Wrong test or a bug?
Custom generators
Using custom generators
Testing more complicated scenarios in a readable way
Finding and fixing bugs in the implementation
Coffee break: Property-based tests
Coffee break explained: Property-based tests
Properties and examples
Requirements coverage
Testing side-effectful requirements
Identifying the right test for the job
Data usage tests
Practicing stubbing external services using IO
Testing and design
Service integration tests
Local servers as Resources in integration tests
Writing isolated integration tests
Integration with a service is a single responsibility
Coffee break: Writing integration tests
Coffee break explained: Writing integration tests
Integration tests take more time
Property-based integration tests
Choosing the right testing approach
Test-driven development
Writing a test for a feature that doesn’t exist
Red green refactor
Making tests green
Adding more red tests
The last TDD iteration
Appendix A: Scala cheat sheet
Appendix B: Functional gems
index
A
B
C
D
E
F
G
H
I
J
L
M
N
O
P
Q
R
S
T
U
V
W
Y
Z