Functional Programming in Java
How functional techniques improve your Java programs
Pierre-Yves Saumont
  • January 2017
  • ISBN 9781617292736
  • 472 pages
  • printed in black & white
ePub + Kindle available Feb 6, 2017

An excellent introduction to functional programming for Java programmers.

Piotr Bzdyl, SmartRecruiters

Functional Programming in Java teaches Java developers how to incorporate the most powerful benefits of functional programming into new and existing Java code. You'll learn to think functionally about coding tasks in Java and use FP to make your applications easier to understand, optimize, maintain, and scale.

Table of Contents detailed table of contents

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


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

C.1.4. Frege

C.1.5. 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

Here's a bold statement: learn functional programming and you'll be a better Java developer. Fortunately, you don't have to master every aspect of FP to get a big payoff. If you take in a few core principles, you'll see an immediate boost in the scalability, readability, and maintainability of your code. And did we mention that you'll have fewer bugs? Let's get started!

About the book

Functional Programming in Java teaches you how to incorporate the powerful benefits of functional programming into new and existing Java code. This book uses easy-to-grasp examples, exercises, and illustrations to teach core FP principles such as referential transparency, immutability, persistence, and laziness. Along the way, you'll discover which of the new functionally inspired features of Java 8 will help you most.

What's inside

  • Writing code that's easier to read and reason about
  • Safer concurrent and parallel programming
  • Handling errors without exceptions
  • Java 8 features like lambdas, method references, and functional interfaces

About the reader

Written for Java developers with no previous FP experience.

About the author

Pierre-Yves Saumont is a seasoned Java developer with three decades of experience designing and building enterprise software. He is an R&D engineer at Alcatel-Lucent Submarine Networks.

  • combo $49.99 pBook + eBook
  • eBook $39.99 pdf + ePub + kindle

FREE domestic shipping on three or more pBooks

Helped me understand the basic concepts of functional programming.

Philippe Charriere, GitHub

The fundamental concepts and mindset of functional programming explained with the help of a non-esoteric programming language like Java.

Sebastian Metzger, snapADDY

Shows you how to write modern Java code.

Alessandro Campeis, Vimar