Functional Programming in Java teaches Java developers how to incorporate the most powerful benefits of functional programming into new and existing Java code. Written from the ground up to meet the needs of professional Java developers who want to introduce functional programming principles into new and legacy projects, this book uses easy to grasp examples and illustrations to teach core FP principles such as referential transparency, immutability, persistence, and laziness. As you read, you'll work through numerous exercises that help you practice and refine your new skills. You'll discover which of the new functionally-inspired features of Java 8 will help you in applying FP principles to your code—as well as which to avoid. In the end, you'll be able to think functionally about coding tasks in Java and use FP to make your applications easier to understand, optimize, maintain, and scale.
1. What is functional programming?
1.1. What is functional programming?
1.2. Writing useful programs with no side effects
1.3. How referential transparency makes programs safer.
1.4. The benefits of Functional Programming
1.5. Using the substitution model to reason about programs
1.6. Applying functional principles to a simple example
1.7. Pushing abstraction to the limit
1.8. Summary
2. Using functions in Java
2.1. What is a function?
2.1.1. Functions in the real world
2.2. Functions in Java
2.2.1. Functional methods
2.2.2. Java functional interfaces and anonymous classes
2.2.3. Composing functions
2.2.4. Polymorphic functions
2.2.5. Simpliflying the code by using lambdas
2.3. Advanced function features
2.3.1. What about functions of several arguments?
2.3.2. Applying curried functions
2.3.3. Higher-order functions
2.3.4. Polymorphic higher-order functions
2.3.5. Using anonymous functions
2.3.6. Local functions
2.3.7. Closures
2.3.8. Partial function application and automatic currying
2.3.9. Switching arguments of partially applied functions
2.3.10. Recursive functions
2.3.11. The identity function
2.4. Java 8 functional interfaces
2.5. Debugging with lambdas
2.6. Summary
3. Making Java more functional
3.1. Making standard control structures functional
3.2. Abstracting control structures
3.2.1. Cleaning up the code
3.2.2. An alternative to if…else
3.3. Abstracting iteration
3.3.1. Abstracting an operation on lists with mapping
3.3.2. Creating lists
3.3.3. Using head and tail operations
3.3.4. Functionally appending to a list
3.3.5. Reducing and folding lists
3.3.6. Composing mappings and mapping compositions
3.3.7. Applying effects to lists
3.3.8. Approaching functional output
3.3.9. Building corecursive lists
3.4. Using the right types
3.4.1. Problems with standard types
3.4.2. Defining value types
3.4.3. The future of value types in Java
3.5. Summary
4. Recursion, corecursion, and memoization
4.1. Understanding corecursion and recursion
4.1.1. Exploring corecursive and recursive addition examples
4.1.2. Implementing recursion in Java
4.1.3. Using tail call elimination
4.1.4. Using tail recursive methods and functions
4.1.5. Abstracting recursion
4.1.6. Using a drop-in replacement for stack-based recursive methods
4.2. Working with recursive functions
4.2.1. Using locally defined functions
4.2.2. Making functions tail recursive
4.2.3. Doubly recursive functions: the Fibonacci example
4.2.4. Making the list methods stack safe and recursive
4.3. Composing a huge number of functions
4.4. Using memoization
4.4.1. Memoization in imperative programming
4.4.2. Memoization in recursive functions
4.4.3. Automatic memoization
4.5. Summary
5. Data handling with lists
5.1. How to classify data collections
5.1.1. Different types of lists
5.1.2. Relative expected lists performance
5.1.3. Trading time against memory space, and time against complexity
5.1.4. In place mutation
5.1.5. Persistent data structures
5.2. An immutable, persistent, singly linked list implementation
5.3. Data sharing in list operations
5.3.1. More lists operations
5.4. Using recursion to fold lists with higher-order functions
5.4.1. Heap based recursive version of foldRight
5.4.2. Mapping and filtering lists
5.5. Summary
6. Dealing with optional data
6.1. Problems with the null pointer
6.2. Alternatives to null references
6.3. The Option data type
6.3.1. Getting a value from an Option
6.3.2. Applying functions to optional values
6.3.3. Dealing with Option composition
6.3.4. Option use cases
6.3.5. Other ways to combine options
6.3.6. Composing List with Option
6.4. Miscellaneous utilities for Option
6.4.1. Testing for Some or None
6.4.2. Equals and hashcode
6.5. How and when to use Option
6.6. Summary
7. Handling errors and exceptions
7.1. The problems to be solved
7.2. The Either type
7.2.1. Composing Either
7.3. The Result type
7.3.1. Adding methods to the Result class
7.4. Result patterns
7.5. Advanced Result handling
7.5.1. Applying predicates
7.5.2. Mapping failures
7.5.3. Adding factory methods
7.5.4. Applying effects
7.5.5. Advanced result composition
7.6. Summary
8. Advanced list handling
8.1. The problem with length
8.1.1. The performance problem
8.1.2. The benefit of memoization
8.1.3. The drawbacks of memoization
8.1.4. Actual performance
8.2. Composing List and Result
8.2.1. Methods on List returning Result
8.2.2. Converting from List<Result> to Result<List>
8.3. Abstracting common list use cases
8.3.1. Zipping and unzipping lists
8.3.2. Accessing elements by their index
8.3.3. Splitting lists
8.3.4. Searching for sublists
8.3.5. Miscellaneous functions for working with lists
8.4. Automatic parallel processing of lists
8.4.1. Not all computations can be parallelized
8.4.2. Breaking the list in sublists
8.4.3. Processing sublists in parallel
8.5. Summary
9. Working with laziness
9.1. Understanding strictness and laziness
9.1.1. Java is a strict language
9.1.2. The problem with strictness
9.2. Implementing laziness
9.3. Things you can't do without laziness
9.4. Why not use the Java 8 stream?
9.5. Creating a lazy list data structure
9.5.1. Memoizing evaluated values
9.5.2. Manipulating streams
9.6. The true essence of laziness
9.6.1. Folding streams
9.6.2. Tracing evaluation and function application
9.7. Handling infinite streams
9.8. Avoiding null references and mutable fields
9.9. Summary
10. More data handling with trees
10.1. The binary tree
10.1.1. Balanced and unbalanced trees
10.1.2. Size, heath and depth
10.1.3. Leafy trees
10.1.4. Ordered binary trees or binary search trees (BST)
10.1.5. Insertion order
10.1.6. Tree traversal order
10.2. Implementing the binary search tree
10.3. Removing elements from trees
10.4. Merging arbitrary trees
10.5. Folding trees
10.5.1. Folding with two functions
10.5.2. Folding with a single function
10.5.3. Which fold implementation to chose?
10.6. Mapping trees
10.7. Balancing trees
10.7.1. Rotating trees
10.7.2. Balancing trees using the Day-Stout-Warren algorithm
10.7.3. Automatically balancing trees
10.7.4. Solving the right problem
10.8. Summary
11. Solving real problems with advanced trees
11.1. Better performance and stack safety with self-balancing trees
11.1.1. The basic tree structure
11.1.2. Inserting an element into the red-black tree
11.1.3. Removing elements from the red-black tree
11.2. A use case for the red-black tree: maps
11.2.1. Implementing Map
11.2.2. Extending maps
11.2.3. Using Map with noncomparable keys
11.3. Implementing a functional priority queue
11.3.1. The priority queue access protocol
11.3.2. Priority queue use cases
11.3.3. Implementation requirements
11.3.4. The leftist heap data structure
11.3.5. Implementing the leftist heap
11.3.6. Implementing the "queue like" interface
11.4. A priority queue for noncomparable elements
11.5. Summary
12. Handling state mutation in a functional way
12.1. A functional random number generator
12.1.1. The random number generator interface
12.1.2. Implementing the random number generator
12.2. A generic API for handling state
12.2.1. Working with state operations
12.2.2. Composing state operations
12.2.3. Recursive state operations
12.3. Generic state handling
12.3.1. State patterns
12.3.2. Building a state machine
12.3.3. When to use State and the State Machine
12.4. Summary
13. Functional Input/Output
13.1. Applying effects in context
13.1.1. What are effects?
13.1.2. Implementing effects
13.1.3. More powerful effects for failures
13.2. Reading data
13.2.1. Reading data from the console
13.2.2. Reading from a file
13.2.3. Testing with input
13.3. Really functional input/output
13.3.1. How can input/output be made fully functional?
13.3.2. Implementing purely functional input/output
13.3.3. Combining IO
13.3.4. Handling input with IO
13.3.5. Extending the IO type
13.3.6. Making the IO type stack safe
13.4. Summary
14. Sharing mutable state with actors
14.1. The Actor model
14.1.1. Asynchronous messaging
14.1.2. Handling parallelization
14.1.3. Handling actor state mutation
14.2. Building the actor framework
14.2.1. Limitations of this actor framework
14.2.2. Designing the actor framework interfaces
14.2.3. The AbstractActor implementation
14.3. Putting actors to work
14.3.1. Implementing the ping pong example
14.3.2. A more serious example: running a computation in parallel
14.3.3. Reordering the results
14.3.4. Fixing the performance problem
14.4. Summary
15. Solving common problems functionally
15.1. Using assertions to validate data
15.2. Reading properties from file
15.2.1. Loading the property file
15.2.2. Reading properties as strings
15.2.3. Producing better error messages
15.2.4. Reading properties as lists
15.2.5. Reading enum values
15.2.6. Reading properties of arbitrary types
15.3. Converting an imperative program: the XML reader
15.3.1. Listing the necessary functions
15.3.2. Composing the functions and applying an effect
15.3.3. Implementing the functions
15.3.4. Making the program even more functional
15.3.5. Fixing the argument type problem
15.3.6. Making the element processing function a parameter
15.3.7. Handling errors on element names
15.4. Summary
Appendixes
Appendix A: Using Java 8 functional features
A.1. The Optional class
A.2. Streams
Appendix B: Monads
Appendix C: Where to go from here
C.1. Choosing a new language
C.1.1. Haskell
C.1.2. Scala
C.1.3. Frege
C.1.4. What about dynamically typed functional languages?
C.2. Staying with Java
C.2.1. Functional Java
C.2.2. Javaslang
C.2.3. Cyclop
C.2.4. Other functional libraries
C.3. Further reading
About the Technology
Functional programming is a deep and potentially mind-bending discipline. Fortunately, as a Java developer you don't have to master every aspect of FP to get a big boost in the performance, readability, and maintainability of your applications. By learning a few core FP principles, you can write code that's less prone to side effects and unwanted dependencies and which is much better suited to the parallel processing required by modern multi-core and distributed systems. Also, because units of functional code are designed to be modular and independent, FP reduces or eliminates many of the bugs development teams routinely face when managing a large codebase.
What's inside
- Understanding higher-order functions
- Increasing productivity through better use of abstraction
- Safer, more natural concurrent and parallel programming
- Composability through functional error processing
- Handling errors without exceptions
- Using new Java 8 features such as Lambdas and Streams wisely
- MEAP combo $49.99 pBook + eBook
- MEAP eBook $39.99 pdf + ePub + kindle
FREE domestic shipping on three or more pBooks