Preface |
|
xi | |
Acknowledgments |
|
xiv | |
About this book |
|
xv | |
About the author |
|
xxi | |
About the cover illustration |
|
xxii | |
|
|
1 | (14) |
|
|
3 | (3) |
|
|
5 | (1) |
|
Making programs safer with referential transparency |
|
|
6 | (1) |
|
1.2 The benefits of safe programming |
|
|
6 | (9) |
|
Using the substitution model to reason about programs |
|
|
8 | (1) |
|
Applying safe principles to a simple example |
|
|
9 | (4) |
|
Pushing abstraction to the limit |
|
|
13 | (2) |
|
2 Functional programming in Kotlin: An overview |
|
|
15 | (31) |
|
2.1 Fields and variables in Kotlin |
|
|
16 | (2) |
|
Omitting the type to simplify |
|
|
16 | (1) |
|
|
16 | (1) |
|
Understanding lazy initialization |
|
|
17 | (1) |
|
2.2 Classes and interfaces in Kotlin |
|
|
18 | (7) |
|
Making the code even more concise |
|
|
19 | (1) |
|
Implementing an interface or extending a class |
|
|
20 | (1) |
|
|
20 | (1) |
|
Overloading property constructors |
|
|
20 | (2) |
|
Creating equals and hashCode methods |
|
|
22 | (1) |
|
Destructuring data objects |
|
|
23 | (1) |
|
Implementing static members in Kotlin |
|
|
23 | (1) |
|
|
24 | (1) |
|
Preventing utility class instantiation |
|
|
24 | (1) |
|
2.3 Kotlin doesn't have primitives |
|
|
25 | (1) |
|
2.4 Kodin's two types of collections |
|
|
25 | (2) |
|
|
27 | (1) |
|
|
27 | (1) |
|
|
28 | (4) |
|
|
28 | (1) |
|
|
29 | (1) |
|
|
30 | (1) |
|
Using extension functions |
|
|
30 | (1) |
|
|
31 | (1) |
|
|
32 | (2) |
|
Dealing with nullable types |
|
|
33 | (1) |
|
Elvis and the default value |
|
|
34 | (1) |
|
2.9 Program flow and control structures |
|
|
34 | (3) |
|
Using conditional selectors |
|
|
34 | (1) |
|
Using multi-conditional selectors |
|
|
35 | (1) |
|
|
36 | (1) |
|
2.10 Kodin's unchecked exceptions |
|
|
37 | (1) |
|
2.11 Automatic resource closure |
|
|
38 | (1) |
|
|
39 | (1) |
|
2.13 Equality versus identity |
|
|
40 | (1) |
|
2.14 String interpolation |
|
|
40 | (1) |
|
|
41 | (1) |
|
2.16 Variance: parameterized types and subtyping |
|
|
41 | (5) |
|
Why is variance a potential problem? |
|
|
41 | (1) |
|
When to use covariance and when to use contravariance |
|
|
42 | (1) |
|
Declaration-site variance versus use-site variance |
|
|
43 | (3) |
|
3 Programming with Junctions |
|
|
46 | (35) |
|
|
47 | (5) |
|
Understanding the relationship between two function sets |
|
|
47 | (2) |
|
An overview of inverse functions in Kotlin |
|
|
49 | (1) |
|
Working with partial functions |
|
|
49 | (1) |
|
Understanding function composition |
|
|
50 | (1) |
|
Using functions of several arguments |
|
|
50 | (1) |
|
|
51 | (1) |
|
Using partially-applied functions |
|
|
51 | (1) |
|
Functions have no effects |
|
|
52 | (1) |
|
|
52 | (10) |
|
Understanding functions as data |
|
|
53 | (1) |
|
Understanding data as functions |
|
|
53 | (1) |
|
Using object constructors as functions |
|
|
53 | (1) |
|
Using Kotlin's fun functions |
|
|
54 | (3) |
|
Using object notation versus functional notation |
|
|
57 | (1) |
|
|
58 | (1) |
|
Using function references |
|
|
59 | (1) |
|
|
60 | (1) |
|
|
61 | (1) |
|
3.3 Advanced function features |
|
|
62 | (19) |
|
What about functions of several arguments? |
|
|
62 | (1) |
|
Applying curried functions |
|
|
63 | (1) |
|
Implementing higher-order functions |
|
|
63 | (1) |
|
Creating polymorphic HOFs |
|
|
64 | (2) |
|
Using anonymous functions |
|
|
66 | (2) |
|
|
68 | (1) |
|
|
68 | (1) |
|
Applying functions partially and automatic currying |
|
|
69 | (4) |
|
Switching arguments of partially-applied functions |
|
|
73 | (1) |
|
Declaring the identity function |
|
|
74 | (1) |
|
|
75 | (6) |
|
4 Recursion, corecursion, and memoization |
|
|
81 | (36) |
|
4.1 Corecursion and recursion |
|
|
82 | (6) |
|
|
82 | (1) |
|
|
83 | (1) |
|
Differentiating recursive and corecursive functions |
|
|
84 | (2) |
|
Choosing recursion or corecursion |
|
|
86 | (2) |
|
4.2 Tail Call Elimination |
|
|
88 | (6) |
|
Using Tail Call Elimination |
|
|
88 | (1) |
|
Switching from loops to corecursion |
|
|
89 | (3) |
|
Using recursive value functions |
|
|
92 | (2) |
|
4.3 Recursive functions and lists |
|
|
94 | (13) |
|
Using doubly recursive functions |
|
|
96 | (3) |
|
Abstracting recursion on lists |
|
|
99 | (3) |
|
|
102 | (1) |
|
Building corecursive lists |
|
|
103 | (3) |
|
|
106 | (1) |
|
|
107 | (8) |
|
Using memoization in loop-based programming |
|
|
107 | (1) |
|
Using memoization in recursive functions |
|
|
108 | (1) |
|
Using implicit memoization |
|
|
109 | (2) |
|
Using automatic memoization |
|
|
111 | (2) |
|
Implementing memoization of multi-argument functions |
|
|
113 | (2) |
|
4.5 Are memoized functions pure? |
|
|
115 | (2) |
|
5 Data handling with lists |
|
|
117 | (32) |
|
5.1 How to classify data collections |
|
|
118 | (1) |
|
5.2 Different types of lists |
|
|
118 | (1) |
|
5.3 Relative expected list performance |
|
|
119 | (3) |
|
Trading time against memory space and complexity |
|
|
120 | (1) |
|
Avoiding in-place mutation |
|
|
121 | (1) |
|
5.4 What kinds of lists are available in Kotlin? |
|
|
122 | (4) |
|
Using persistent data structures |
|
|
122 | (1) |
|
Implementing immutable, persistent, singly linked lists |
|
|
123 | (3) |
|
5.5 Data sharing in list operations |
|
|
126 | (2) |
|
|
128 | (21) |
|
Benefiting from object notation |
|
|
129 | (2) |
|
|
131 | (2) |
|
Dropping from the end of a list |
|
|
133 | (1) |
|
Using recursion to fold lists with higher-order functions (HOFs) |
|
|
134 | (1) |
|
|
135 | (9) |
|
Creating a stack-safe recursive version of foldRight |
|
|
144 | (2) |
|
Mapping and filtering lists |
|
|
146 | (3) |
|
6 Dealing with optional data |
|
|
149 | (22) |
|
6.1 Problems with the null pointer |
|
|
150 | (2) |
|
6.2 How Kodin handles null references |
|
|
152 | (1) |
|
6.3 Alternatives to null references |
|
|
153 | (3) |
|
6.4 Using the Option type |
|
|
156 | (15) |
|
Getting a value from an Option |
|
|
158 | (1) |
|
Applying functions to optional values |
|
|
159 | (1) |
|
Dealing with Option composition |
|
|
160 | (1) |
|
|
161 | (4) |
|
Other ways to combine options |
|
|
165 | (2) |
|
Composing List with Option |
|
|
167 | (2) |
|
Using Option and when to do so |
|
|
169 | (2) |
|
7 Handling errors and exceptions |
|
|
171 | (24) |
|
7.1 The problems with missing data |
|
|
172 | (1) |
|
|
173 | (3) |
|
|
176 | (2) |
|
|
178 | (6) |
|
7.5 Advanced Result handling |
|
|
184 | (2) |
|
|
184 | (2) |
|
|
186 | (1) |
|
7.7 Adding factory functions |
|
|
186 | (1) |
|
|
187 | (3) |
|
7.9 Advanced result composition |
|
|
190 | (5) |
|
|
195 | (30) |
|
8.1 The problem with length |
|
|
196 | (1) |
|
8.2 The performance problem |
|
|
196 | (1) |
|
8.3 The benefits of memoization |
|
|
197 | (2) |
|
Handling memoization drawbacks |
|
|
197 | (2) |
|
Evaluating performance improvements |
|
|
199 | (1) |
|
8.4 List and Result composition |
|
|
199 | (4) |
|
Handling lists returning Result |
|
|
199 | (2) |
|
Converting from List<Result> to Result<List> |
|
|
201 | (2) |
|
8.5 Common List abstractions |
|
|
203 | (17) |
|
Zipping and unzipping lists |
|
|
204 | (2) |
|
Accessing elements by their index |
|
|
206 | (5) |
|
|
211 | (3) |
|
|
214 | (1) |
|
Miscellaneous functions for working with lists |
|
|
215 | (5) |
|
8.6 Automatic parallel processing of lists |
|
|
220 | (5) |
|
Not all computations can be parallelized |
|
|
220 | (1) |
|
Breaking the list into sublists |
|
|
220 | (2) |
|
Processing sublists in parallel |
|
|
222 | (3) |
|
|
225 | (36) |
|
9.1 Strictness versus laziness |
|
|
226 | (1) |
|
9.2 Kotlin and strictness |
|
|
227 | (2) |
|
|
229 | (1) |
|
9.4 Laziness implementations |
|
|
230 | (12) |
|
|
232 | (3) |
|
|
235 | (2) |
|
Mapping and flatMapping Lazy |
|
|
237 | (2) |
|
|
239 | (1) |
|
|
240 | (2) |
|
9.5 Further lazy compositions |
|
|
242 | (5) |
|
|
242 | (2) |
|
Things you can't do without laziness |
|
|
244 | (1) |
|
Creating a lazy list data structure |
|
|
245 | (2) |
|
|
247 | (14) |
|
|
253 | (3) |
|
Tracing evaluation and function application |
|
|
256 | (1) |
|
Applying streams to concrete problems |
|
|
257 | (4) |
|
10 More data handling with trees |
|
|
261 | (37) |
|
|
262 | (1) |
|
10.2 Understanding balanced and unbalanced trees |
|
|
263 | (1) |
|
10.3 Looking at size, height, and depth in trees |
|
|
263 | (1) |
|
10.4 Empty trees and the recursive definition |
|
|
264 | (1) |
|
|
264 | (1) |
|
10.6 Ordered binary trees or binary search trees |
|
|
265 | (1) |
|
10.7 Insertion order and the structure of trees |
|
|
266 | (1) |
|
10.8 Recursive and non-recursive tree traversal order |
|
|
267 | (2) |
|
Traversing recursive trees |
|
|
267 | (2) |
|
Non-recursive traversal orders |
|
|
269 | (1) |
|
10.9 Binary search tree implementation |
|
|
269 | (14) |
|
Understanding variance and trees |
|
|
270 | (2) |
|
What about an abstract function in the Tree class? |
|
|
272 | (1) |
|
|
272 | (1) |
|
|
273 | (3) |
|
Removing elements from trees |
|
|
276 | (2) |
|
|
278 | (5) |
|
10.10 About folding trees |
|
|
283 | (6) |
|
Folding with two functions |
|
|
284 | (2) |
|
Folding with a single function |
|
|
286 | (1) |
|
Choosing a fold implementation |
|
|
287 | (2) |
|
10.11 About mapping trees |
|
|
289 | (1) |
|
10.12 About balancing trees |
|
|
290 | (8) |
|
|
290 | (2) |
|
Using the Day-Stout-Warren algorithm |
|
|
292 | (4) |
|
Automatically balancing trees |
|
|
296 | (2) |
|
11 Solving problems with advanced trees |
|
|
298 | (27) |
|
11.1 Better performance and stack safety with self-balancing trees |
|
|
299 | (8) |
|
Understanding the basic red-black tree structure |
|
|
299 | (2) |
|
Adding an element to the red-black tree |
|
|
301 | (6) |
|
Removing elements from the red-black tree |
|
|
307 | (1) |
|
11.2 A use case for the red-black tree: Maps |
|
|
307 | (6) |
|
|
307 | (2) |
|
|
309 | (2) |
|
Using Map with noncomparable keys |
|
|
311 | (2) |
|
11.3 Implementing a functional priority queue |
|
|
313 | (6) |
|
Looking at the priority queue access protocol |
|
|
313 | (1) |
|
Exploring priority queue use cases |
|
|
313 | (1) |
|
Looking at implementation requirements |
|
|
314 | (1) |
|
The leftist heap data structure |
|
|
314 | (1) |
|
Implementing the leftist heap |
|
|
315 | (3) |
|
Implementing the queue-like interface |
|
|
318 | (1) |
|
11.4 Elements and sorted lists |
|
|
319 | (2) |
|
11.5 A priority queue for noncomparable elements |
|
|
321 | (4) |
|
12 Functional input/output |
|
|
325 | (26) |
|
12.1 What does effects in context mean? |
|
|
326 | (4) |
|
|
326 | (1) |
|
|
327 | (3) |
|
|
330 | (6) |
|
Reading data from the console |
|
|
330 | (5) |
|
|
335 | (1) |
|
|
336 | (1) |
|
12.4 Fully functional input/output |
|
|
337 | (14) |
|
Making input/output fully functional |
|
|
337 | (1) |
|
Implementing purely functional I/O |
|
|
338 | (1) |
|
|
339 | (1) |
|
|
340 | (3) |
|
|
343 | (2) |
|
Making the IO type stack-safe |
|
|
345 | (6) |
|
13 Sharing mutable states with actors |
|
|
351 | (24) |
|
|
352 | (2) |
|
Understanding asynchronous messaging |
|
|
353 | (1) |
|
|
353 | (1) |
|
Handling actor state mutation |
|
|
353 | (1) |
|
13.2 An actor framework implementation |
|
|
354 | (3) |
|
Understanding the limitations |
|
|
355 | (1) |
|
Designing the actor framework interfaces |
|
|
355 | (2) |
|
13.3 The AbstractActor implementation |
|
|
357 | (1) |
|
13.4 Putting actors to work |
|
|
358 | (17) |
|
Implementing the Ping Pong example |
|
|
358 | (3) |
|
Running a computation in parallel |
|
|
361 | (4) |
|
|
365 | (3) |
|
|
368 | (7) |
|
14 A Solving common problems functionally |
|
|
375 | (32) |
|
14.1 Assertions and data validation |
|
|
376 | (4) |
|
14.2 Retries for functions and effects |
|
|
380 | (2) |
|
14.3 Reading properties from a file |
|
|
382 | (10) |
|
Loading the property file |
|
|
383 | (1) |
|
Reading properties as strings |
|
|
383 | (2) |
|
Producing better error messages |
|
|
385 | (2) |
|
Reading properties as lists |
|
|
387 | (2) |
|
|
389 | (1) |
|
Reading properties of arbitrary types |
|
|
390 | (2) |
|
14.4 Converting an imperative program: The XML reader |
|
|
392 | (15) |
|
Step 1 The imperative solution |
|
|
392 | (2) |
|
Step 2 Making an imperative program more functional |
|
|
394 | (3) |
|
Step 3 Making the program even more functional |
|
|
397 | (4) |
|
Step 4 Fixing the argument type problem |
|
|
401 | (1) |
|
Step 5 Making the element-processing function a parameter |
|
|
402 | (1) |
|
Step 6 Handling errors on element names |
|
|
403 | (1) |
|
Step 7 Additional improvements to the formerly imperative code |
|
|
404 | (3) |
Appendix A |
|
407 | (20) |
Appendix B |
|
427 | (16) |
Index |
|
443 | |