The Joy of Kotlin

Book description

Maintaining poor legacy code, interpreting cryptic comments, and writing the same boilerplate over and over can suck the joy out of your life as a Java developer. Fear not! There's hope! Kotlin is an elegant JVM language with modern features and easy integration with Java. The Joy of Kotlin teaches you practical techniques to improve abstraction and design, to write comprehensible code, and to build maintainable bug-free applications.



About the Technology

Your programming language should be expressive, safe, flexible, and intuitive, and Kotlin checks all the boxes! This elegant JVM language integrates seamlessly with Java, and makes it a breeze to switch between OO and functional styles of programming. It’s also fully supported by Google as a first-class Android language. Master the powerful techniques in this unique book, and you’ll be able to take on new challenges with increased confidence and skill.



About the Book

The Joy of Kotlin teaches you to write comprehensible, easy-to-maintain, safe programs with Kotlin. In this expert guide, seasoned engineer Pierre-Yves Saumont teaches you to approach common programming challenges with a fresh, FP-inspired perspective. As you work through the many examples, you’ll dive deep into handling errors and data properly, managing state, and taking advantage of laziness. The author’s down-to-earth examples and experience-driven insights will make you a better—and more joyful—developer!



What's Inside

  • Programming with functions
  • Dealing with optional data
  • Safe handling of errors and exceptions
  • Handling and sharing state mutation


About the Reader

Written for intermediate Java or Kotlin developers.



About the Author

Pierre-Yves Saumont is a senior software engineer at Alcatel-Submarine Networks. He’s the author of Functional Programming in Java (Manning, 2017).



Quotes
A fabulous introduction to the universe of functional programming!
- Aleksei Slaikovskii, Oracle

Excellent, easy-to-understand examples of implementing functional programming in Kotlin.
- Emmanuel Medina, Global HITSS

A great handbook for anyone studying Kotlin or functional programming who isn’t sure where to start. Provides solid problems and explanations that don’t underestimate readers.
- Bridger Howell, SoFi

As a merger of both Kotlin and FP, this book teaches only the theory you need and features lots of solved exercises. Give it a try!
- Jean-François Morin, Laval University

Table of contents

  1. The Joy of Kotlin
  2. Copyright
  3. Brief contents
  4. contents
  5. Front matter
    1. preface
    2. acknowledgments
    3. about this book
      1. Who should read this book
      2. What you’ll learn
      3. Pushing abstraction further
      4. Immutability
      5. Referential transparency
      6. Encapsulated state mutation sharing
      7. Abstracting control flow and control structures
      8. Using the right types
      9. Laziness
      10. Audience
      11. How this book is organized: A roadmap
      12. Completing the exercises
      13. Learning the techniques in this book
      14. About the code
      15. liveBook discussion
    4. about the author
    5. about the cover illustration
  6. 1 Making programs safer
    1. 1.1 Programming traps
      1. 1.1.1 Safely handling effects
      2. 1.1.2 Making programs safer with referential transparency
    2. 1.2 The benefits of safe programming
      1. 1.2.1 Using the substitution model to reason about programs
      2. 1.2.2 Applying safe principles to a simple example
      3. 1.2.3 Pushing abstraction to the limit
    3. Summary
  7. 2 Functional programming in Kotlin: An overview
    1. 2.1 Fields and variables in Kotlin
      1. 2.1.1 Omitting the type to simplify
      2. 2.1.2 Using mutable fields
      3. 2.1.3 Understanding lazy initialization
    2. 2.2 Classes and interfaces in Kotlin
      1. 2.2.1 Making the code even more concise
      2. 2.2.2 Implementing an interface or extending a class
      3. 2.2.3 Instantiating a class
      4. 2.2.4 Overloading property constructors
      5. 2.2.5 Creating equals and hashCode methods
      6. 2.2.6 Destructuring data objects
      7. 2.2.7 Implementing static members in Kotlin
      8. 2.2.8 Using singletons
      9. 2.2.9 Preventing utility class instantiation
    3. 2.3 Kotlin doesn’t have primitives
    4. 2.4 Kotlin’s two types of collections
    5. 2.5 Kotlin’s packages
    6. 2.6 Visibility in Kotlin
    7. 2.7 Functions in Kotlin
      1. 2.7.1 Declaring functions
      2. 2.7.2 Using local functions
      3. 2.7.3 Overriding functions
      4. 2.7.4 Using extension functions
      5. 2.7.5 Using lambdas
    8. 2.8 Nulls in Kotlin
      1. 2.8.1 Dealing with nullable types
      2. 2.8.2 Elvis and the default value
    9. 2.9 Program flow and control structures
      1. 2.9.1 Using conditional selectors
      2. 2.9.2 Using multi-conditional selectors
      3. 2.9.3 Using loops
    10. Kotlin’s unchecked exceptions
    11. Automatic resource closure
    12. Kotlin’s smart casts
    13. Equality versus identity
    14. String interpolation
    15. Multi-line strings
    16. Variance: parameterized types and subtyping
      1. 2.16.1 Why is variance a potential problem?
      2. 2.16.2 When to use covariance and when to use contravariance
      3. 2.16.3 Declaration-site variance versus use-site variance
    17. Summary
  8. 3 Programming with functions
    1. 3.1 What’s a function?
      1. 3.1.1 Understanding the relationship between two function sets
      2. 3.1.2 An overview of inverse functions in Kotlin
      3. 3.1.3 Working with partial functions
      4. 3.1.4 Understanding function composition
      5. 3.1.5 Using functions of several arguments
      6. 3.1.6 Currying functions
      7. 3.1.7 Using partially-applied functions
      8. 3.1.8 Functions have no effects
    2. 3.2 Functions in Kotlin
      1. 3.2.1 Understanding functions as data
      2. 3.2.2 Understanding data as functions
      3. 3.2.3 Using object constructors as functions
      4. 3.2.4 Using Kotlin’s fun functions
    3. Exactness
      1. 3.2.5 Using object notation versus functional notation
      2. 3.2.6 Using value functions
      3. 3.2.7 Using function references
      4. 3.2.8 Composing functions
      5. 3.2.9 Reusing functions
    4. 3.3 Advanced function features
      1. 3.3.1 What about functions of several arguments?
      2. 3.3.2 Applying curried functions
      3. 3.3.3 Implementing higher-order functions
      4. 3.3.4 Creating polymorphic HOFs
    5. Testing function parameters
      1. 3.3.5 Using anonymous functions
      2. 3.3.6 Defining local functions
      3. 3.3.7 Implementing closures
      4. 3.3.8 Applying functions partially and automatic currying
      5. 3.3.9 Switching arguments of partially-applied functions
      6. Declaring the identity function
      7. Using the right types
    6. Summary
  9. 4 Recursion, corecursion, and memoization
    1. 4.1 Corecursion and recursion
      1. 4.1.1 Implementing corecursion
      2. 4.1.2 Implementing recursion
      3. 4.1.3 Differentiating recursive and corecursive functions
      4. 4.1.4 Choosing recursion or corecursion
    2. 4.2 Tail Call Elimination
      1. 4.2.1 Using Tail Call Elimination
      2. 4.2.2 Switching from loops to corecursion
      3. 4.2.3 Using recursive value functions
    3. 4.3 Recursive functions and lists
      1. 4.3.1 Using doubly recursive functions
      2. 4.3.2 Abstracting recursion on lists
      3. 4.3.3 Reversing a list
      4. 4.3.4 Building corecursive lists
      5. 4.3.5 The danger of strictness
    4. 4.4 Memoization
      1. 4.4.1 Using memoization in loop-based programming
      2. 4.4.2 Using memoization in recursive functions
      3. 4.4.3 Using implicit memoization
      4. 4.4.4 Using automatic memoization
      5. 4.4.5 Implementing memoization of multi-argument functions
    5. 4.5 Are memoized functions pure?
    6. Summary
  10. 5 Data handling with lists
    1. 5.1 How to classify data collections
    2. 5.2 Different types of lists
    3. 5.3 Relative expected list performance
      1. 5.3.1 Trading time against memory space and complexity
      2. 5.3.2 Avoiding in-place mutation
    4. Update in place
    5. 5.4 What kinds of lists are available in Kotlin?
      1. 5.4.1 Using persistent data structures
      2. 5.4.2 Implementing immutable, persistent, singly linked lists
    6. 5.5 Data sharing in list operations
    7. 5.6 More list operations
      1. 5.6.1 Benefiting from object notation
      2. 5.6.2 Concatenating lists
      3. 5.6.3 Dropping from the end of a list
      4. 5.6.4 Using recursion to fold lists with higher-order functions (HOFs)
      5. 5.6.5 Using variance
      6. 5.6.6 Creating a stack-safe recursive version of foldRight
      7. 5.6.7 Mapping and filtering lists
    8. Summary
  11. 6 Dealing with optional data
    1. 6.1 Problems with the null pointer
    2. 6.2 How Kotlin handles null references
    3. 6.3 Alternatives to null references
    4. 6.4 Using the Option type
      1. 6.4.1 Getting a value from an Option
      2. 6.4.2 Applying functions to optional values
      3. 6.4.3 Dealing with Option composition
      4. 6.4.4 Option use cases
      5. 6.4.5 Other ways to combine options
      6. 6.4.6 Composing List with Option
      7. 6.4.7 Using Option and when to do so
    5. Summary
  12. 7 Handling errors and exceptions
    1. 7.1 The problems with missing data
    2. 7.2 The Either type
    3. 7.3 The Result type
    4. 7.4 Result patterns
    5. 7.5 Advanced Result handling
      1. 7.5.1 Applying predicates
    6. 7.6 Mapping failures
    7. 7.7 Adding factory functions
    8. 7.8 Applying effects
    9. 7.9 Advanced result composition
    10. Summary
  13. 8 Advanced list handling
    1. 8.1 The problem with length
    2. 8.2 The performance problem
    3. 8.3 The benefits of memoization
      1. 8.3.1 Handling memoization drawbacks
      2. 8.3.2 Evaluating performance improvements
    4. 8.4 List and Result composition
      1. 8.4.1 Handling lists returning Result
      2. 8.4.2 Converting from List<Result> to Result<List>
    5. 8.5 Common List abstractions
      1. 8.5.1 Zipping and unzipping lists
      2. 8.5.2 Accessing elements by their index
    6. The zero element
      1. 8.5.3 Splitting lists
    7. When not to use folds
      1. 8.5.4 Searching for sublists
      2. 8.5.5 Miscellaneous functions for working with lists
    8. 8.6 Automatic parallel processing of lists
      1. 8.6.1 Not all computations can be parallelized
      2. 8.6.2 Breaking the list into sublists
      3. 8.6.3 Processing sublists in parallel
    9. Summary
  14. 9 Working with laziness
    1. 9.7 Strictness versus laziness
    2. 9.8 Kotlin and strictness
    3. 9.9 Kotlin and laziness
    4. Laziness implementations
      1. 9.10.1 Composing lazy values
      2. 9.10.2 Lifting functions
      3. 9.10.3 Mapping and flatMapping Lazy
      4. 9.10.4 Composing Lazy with List
      5. 9.10.5 Dealing with exceptions
    5. Further lazy compositions
      1. 9.11.1 Lazily applying effects
      2. 9.11.2 Things you can’t do without laziness
      3. 9.11.3 Creating a lazy list data structure
    6. Handling streams
      1. 9.12.1 Folding streams
      2. 9.12.2 Tracing evaluation and function application
      3. 9.12.3 Applying streams to concrete problems
    7. Summary
  15. 10 More data handling with trees
    1. 10.1 The binary tree
    2. 10.2 Understanding balanced and unbalanced trees
    3. 10.3 Looking at size, height, and depth in trees
    4. 10.4 Empty trees and the recursive definition
    5. 10.5 Leafy trees
    6. 10.6 Ordered binary trees or binary search trees
    7. 10.7 Insertion order and the structure of trees
    8. 10.8 Recursive and non-recursive tree traversal order
      1. 10.8.1 Traversing recursive trees
      2. 10.8.2 Non-recursive traversal orders
    9. 10.9 Binary search tree implementation
      1. 10.9.1 Understanding variance and trees
      2. 10.9.2 What about an abstract function in the Tree class?
      3. 10.9.3 Overloading operators
      4. 10.9.4 Recursion in trees
      5. 10.9.5 Removing elements from trees
      6. 10.9.6 Merging arbitrary trees
    10. About folding trees
      1. 10.10.1 Folding with two functions
      2. 10.10.2 Folding with a single function
      3. 10.10.3 Choosing a fold implementation
    11. About mapping trees
    12. About balancing trees
      1. 10.12.1 Rotating trees
      2. 10.12.2 Using the Day-Stout-Warren algorithm
      3. 10.12.3 Automatically balancing trees
    13. Summary
  16. 11 Solving problems with advanced trees
    1. 11.1 Better performance and stack safety with self-balancing trees
      1. 11.1.1 Understanding the basic red-black tree structure
      2. 11.1.2 Adding an element to the red-black tree
      3. 11.1.3 Removing elements from the red-black tree
    2. 11.2 A use case for the red-black tree: Maps
      1. 11.2.1 Implementing Map
      2. 11.2.2 Extending maps
      3. 11.2.3 Using Map with noncomparable keys
    3. 11.3 Implementing a functional priority queue
      1. 11.3.1 Looking at the priority queue access protocol
      2. 11.3.2 Exploring priority queue use cases
      3. 11.3.3 Looking at implementation requirements
      4. 11.3.4 The leftist heap data structure
      5. 11.3.5 Implementing the leftist heap
      6. 11.3.6 Implementing the queue-like interface
    4. 11.4 Elements and sorted lists
    5. 11.5 A priority queue for noncomparable elements
    6. Summary
  17. 12 Functional input/output
    1. 12.1 What does effects in context mean?
      1. 12.1.1 Handling effects
      2. 12.1.2 Implementing effects
    2. 12.2 Reading data
      1. 12.2.1 Reading data from the console
      2. 12.2.2 Reading from a file
    3. 12.3 Testing with input
    4. 12.4 Fully functional input/output
      1. 12.4.1 Making input/output fully functional
      2. 12.4.2 Implementing purely functional I/O
      3. 12.4.3 Combining I/O
      4. 12.4.4 Handling input with IO
      5. 12.4.5 Extending the IO type
      6. 12.4.6 Making the IO type stack-safe
    5. Summary
  18. 13 Sharing mutable states with actors
    1. 13.1 The actor model
      1. 13.1.1 Understanding asynchronous messaging
      2. 13.1.2 Handling parallelization
      3. 13.1.3 Handling actor state mutation
    2. 13.2 An actor framework implementation
      1. 13.2.1 Understanding the limitations
      2. 13.2.2 Designing the actor framework interfaces
    3. 13.3 The AbstractActor implementation
    4. 13.4 Putting actors to work
      1. 13.4.1 Implementing the Ping Pong example
      2. 13.4.2 Running a computation in parallel
      3. 13.4.3 Reordering the results
      4. 13.4.4 Optimizing performance
    5. Summary
  19. 14 Solving common problems functionally
    1. 14.1 Assertions and data validation
    2. 14.2 Retries for functions and effects
    3. 14.3 Reading properties from a file
      1. 14.3.1 Loading the property file
      2. 14.3.2 Reading properties as strings
      3. 14.3.3 Producing better error messages
      4. 14.3.4 Reading properties as lists
      5. 14.3.5 Reading enum values
      6. 14.3.6 Reading properties of arbitrary types
    4. 14.4 Converting an imperative program: The XML reader
      1. 14.4.1 Step 1: The imperative solution
      2. 14.4.2 Step 2: Making an imperative program more functional
      3. 14.4.3 Step 3: Making the program even more functional
      4. 14.4.4 Step 4: Fixing the argument type problem
      5. 14.4.5 Step 5: Making the element-processing function a parameter
      6. 14.4.6 Step 6: Handling errors on element names
      7. 14.4.7 Step 7: Additional improvements to the formerly imperative code
    5. Summary
  20. Appendix A. Mixing Kotlin with Java
    1. Creating and managing mixed projects
      1. Creating a simple project with Gradle
      2. Importing your Gradle project into IntelliJ
      3. Adding dependencies to your project
      4. Creating multi-module projects
      5. Adding dependencies to a multiple module project
    2. Java library methods and Kotlin code
      1. Using Java primitives
      2. Using Java numerical object types
      3. Failing fast on null values
      4. Using Kotlin and Java string types
      5. Implementing other type conversions
      6. Using Java varargs
      7. Specifying nullability in Java
      8. Calling getters and setters
      9. Accessing Java properties with reserved names
      10. Calling checked exceptions
    3. SAM interfaces
    4. Kotlin functions and Java code
      1. Converting Kotlin properties
      2. Using Kotlin public fields
      3. Static fields
      4. Calling Kotlin functions as Java methods
      5. Converting types from Kotlin to Java
      6. Function types
    5. Specific problems with mixed Kotlin/Java projects
    6. Summary
  21. Appendix B. Property-based testing in Kotlin
    1. Why property-based testing?
      1. Writing the interface
      2. Writing the tests
    2. What is property-based testing?
    3. Abstraction and property-based tests
    4. Dependencies for property-based unit testing
    5. Writing property based tests
      1. Creating your own custom generators
      2. Using custom generators
      3. Simplifying the code through further abstraction
    6. Summary
  22. Index
  23. Lists of Figures, Tables and Listings
    1. List of Illustrations
    2. List of Tables
    3. List of Listings

Product information

  • Title: The Joy of Kotlin
  • Author(s): Pierre-Yves Saumont
  • Release date: April 2019
  • Publisher(s): Manning Publications
  • ISBN: 9781617295362