Foreword |
|
xv | |
Preface |
|
xvii | |
Acknowledgments |
|
xxiv | |
About This Book |
|
xxvi | |
About The Author |
|
xxxii | |
About The Cover Illustration |
|
xxxiii | |
|
|
1 | (96) |
|
|
3 | (15) |
|
1.1 Solving problems in the GHCi REPL with functions |
|
|
4 | (2) |
|
1.2 From GHCi and String to GHC and Text |
|
|
6 | (1) |
|
1.3 Functional programs as sets of IO actions |
|
|
7 | (3) |
|
1.4 Embracing pure functions |
|
|
10 | (8) |
|
Separating I/O from pure functions |
|
|
10 | (3) |
|
Computing the most frequent words by sorting them |
|
|
13 | (1) |
|
|
14 | (3) |
|
Rule them all with IO actions |
|
|
17 | (1) |
|
|
18 | (42) |
|
2.1 Manipulating a radar antenna with type classes |
|
|
19 | (19) |
|
|
19 | (2) |
|
Rotating a radar antenna with Eq, Enum, and Bounded |
|
|
21 | (5) |
|
Combining turns with Semigroup and Monoid |
|
|
26 | (4) |
|
Printing and reading data with Show and Read |
|
|
30 | (3) |
|
Testing functions with Ord and Random |
|
|
33 | (5) |
|
2.2 Issues with numbers and text |
|
|
38 | (13) |
|
Numeric types and type classes |
|
|
38 | (2) |
|
|
40 | (1) |
|
Computing with fixed precision |
|
|
41 | (2) |
|
|
43 | (4) |
|
Converting recursive types to strings |
|
|
47 | (4) |
|
2.3 Abstracting computations with type classes |
|
|
51 | (9) |
|
An idea of a computational context and a common behavior |
|
|
51 | (2) |
|
Exploring different contexts in parallel |
|
|
53 | (1) |
|
|
54 | (2) |
|
|
56 | (4) |
|
3 Developing An Application: Stock Quotes |
|
|
60 | (37) |
|
|
61 | (7) |
|
|
62 | (1) |
|
|
62 | (2) |
|
|
64 | (4) |
|
3.2 Exploring design space |
|
|
68 | (7) |
|
Designing the user interface |
|
|
69 | (1) |
|
|
70 | (2) |
|
|
72 | (1) |
|
|
73 | (1) |
|
Project dependencies overview |
|
|
74 | (1) |
|
3.3 Implementation details |
|
|
75 | (22) |
|
|
76 | (4) |
|
|
80 | (5) |
|
|
85 | (7) |
|
Implementing the user interface |
|
|
92 | (2) |
|
|
94 | (3) |
|
PART 2 INTRODUCTION TO APPLICATION DESIGN |
|
|
97 | (106) |
|
4 Haskell Development Urith Modules, Packages, And Projects |
|
|
99 | (33) |
|
4.1 Organizing Haskell code with modules |
|
|
100 | (11) |
|
Module structure, imports and exports, and module hierarchy |
|
|
100 | (5) |
|
|
105 | (2) |
|
|
107 | (4) |
|
4.2 Understanding Haskell packages |
|
|
111 | (10) |
|
Packages at the GHC level |
|
|
111 | (3) |
|
Cabal packages and Hackage |
|
|
114 | (7) |
|
4.3 Tools for project development |
|
|
121 | (11) |
|
|
122 | (5) |
|
Haskell projects as a collection of packages |
|
|
127 | (2) |
|
Common project management activities and tools |
|
|
129 | (3) |
|
5 Monads As Practical Functionality Providers |
|
|
132 | (38) |
|
5.1 Basic monads in use: Maybe, Reader, Writer |
|
|
133 | (14) |
|
Maybe monad as a line saver |
|
|
133 | (3) |
|
Carrying configuration all over the program with Reader |
|
|
136 | (4) |
|
|
140 | (7) |
|
5.2 Maintaining state via the State monad |
|
|
147 | (15) |
|
Basic examples with the State monad |
|
|
148 | (4) |
|
Parsing arithmetic expressions with State |
|
|
152 | (8) |
|
RWS monad to rule them all: The game of dice |
|
|
160 | (2) |
|
5.3 Other approaches to mutability |
|
|
162 | (8) |
|
Mutable references in the IO monad |
|
|
162 | (4) |
|
Mutable references in the ST monad |
|
|
166 | (4) |
|
6 Structuring Programs With Monad Transformers |
|
|
170 | (33) |
|
6.1 The problem of combining monads |
|
|
171 | (8) |
|
Evaluating expressions in reverse Polish notation |
|
|
171 | (3) |
|
Introducing monad transformers and monad stacks |
|
|
174 | (5) |
|
6.2 IO-based monad transformer stacks |
|
|
179 | (11) |
|
|
182 | (2) |
|
Exploiting monad stack functionality |
|
|
184 | (4) |
|
|
188 | (1) |
|
Can we do it without RWST? |
|
|
189 | (1) |
|
6.3 What is a monad transformer? |
|
|
190 | (9) |
|
Step 0 Defining a type for a transformer |
|
|
191 | (1) |
|
Step 1 Turning a monad stack into a monad |
|
|
191 | (4) |
|
Step 2 Implementing the full monad stack functionality |
|
|
195 | (2) |
|
Step 3 Supplying additional functionality |
|
|
197 | (1) |
|
|
198 | (1) |
|
6.4 Monad transformers in the Haskell libraries |
|
|
199 | (4) |
|
Identity is where it all starts |
|
|
199 | (1) |
|
An overview of the most common monad transformers |
|
|
200 | (3) |
|
|
203 | (138) |
|
7 Error Handling And Logging |
|
|
205 | (41) |
|
7.1 Overview of error-handling mechanisms in Haskell |
|
|
206 | (4) |
|
|
206 | (2) |
|
|
208 | (1) |
|
Programmable exceptions vs. GHC runtime exceptions |
|
|
209 | (1) |
|
7.2 Programmable exceptions in monad stacks |
|
|
210 | (6) |
|
The Except T monad transformer |
|
|
211 | (1) |
|
Example: Evaluating RPN expressions |
|
|
211 | (5) |
|
7.3 GHC runtime exceptions |
|
|
216 | (6) |
|
An idea of extensible exceptions |
|
|
216 | (2) |
|
|
218 | (1) |
|
|
219 | (3) |
|
7.4 Example: Accessing web APIs and GHC exceptions |
|
|
222 | (19) |
|
|
225 | (8) |
|
Exception-handling strategies |
|
|
233 | (8) |
|
|
241 | (5) |
|
An overview of the monad-logger library |
|
|
243 | (1) |
|
Introducing logging with monad-logger into the suntimes project |
|
|
244 | (2) |
|
|
246 | (35) |
|
8.1 Setting a scene: IPv4 filtering application |
|
|
247 | (5) |
|
Development process overview |
|
|
247 | (1) |
|
|
248 | (4) |
|
8.2 Testing the IPv4 filtering application |
|
|
252 | (24) |
|
Overview of approaches to testing |
|
|
253 | (1) |
|
Testing Cabal projects with tasty |
|
|
253 | (2) |
|
Specifications writing and checking with Hspec |
|
|
255 | (7) |
|
Property-based testing with Hedgehog |
|
|
262 | (10) |
|
Golden tests with tasty-golden |
|
|
272 | (4) |
|
8.3 Other approaches to testing |
|
|
276 | (5) |
|
Testing functions a la the REPL with doctest |
|
|
276 | (2) |
|
Lightweight verification with Liquid Haskell |
|
|
278 | (1) |
|
|
279 | (2) |
|
9 Haskell Data And Code At Run Time |
|
|
281 | (60) |
|
9.1 A mental model for Haskell memory usage at run time |
|
|
282 | (11) |
|
General memory structure and closures |
|
|
282 | (2) |
|
Primitive unboxed data types |
|
|
284 | (1) |
|
Representing data and code in memory with closures |
|
|
285 | (5) |
|
A detour: Lifted types and the concept of strictness |
|
|
290 | (3) |
|
9.2 Control over evaluation and memory usage |
|
|
293 | (8) |
|
Controlling strictness and laziness |
|
|
293 | (6) |
|
Defining data types with unboxed values |
|
|
299 | (2) |
|
9.3 Exploring compiler optimizations by example |
|
|
301 | (10) |
|
|
302 | (6) |
|
|
308 | (2) |
|
Benchmarking and profiling |
|
|
310 | (1) |
|
10.1 Benchmarking functions with criterion |
|
|
311 | (16) |
|
Benchmarking implementations of a simple function |
|
|
311 | (5) |
|
Benchmarking an IPv4 filtering application |
|
|
316 | (11) |
|
10.2 Profiling execution time and memory usage |
|
|
327 | (7) |
|
Simulating iplookup usage in the real world |
|
|
328 | (1) |
|
Analyzing execution time and memory allocation |
|
|
329 | (4) |
|
|
333 | (1) |
|
10.3 Tuning performance of the IPv4 filtering application |
|
|
334 | (7) |
|
Choosing the right data structure |
|
|
335 | (1) |
|
Squeezing parseIP performance |
|
|
336 | (5) |
|
|
341 | (136) |
|
|
343 | (44) |
|
|
344 | (12) |
|
|
344 | (4) |
|
Delivering information with types |
|
|
348 | (7) |
|
|
355 | (1) |
|
11.2 Data kinds and type-level literals |
|
|
356 | (6) |
|
Promoting types to kinds and values to types |
|
|
356 | (2) |
|
|
358 | (4) |
|
11.3 Computations over types with type families |
|
|
362 | (10) |
|
Open and closed type synonym families |
|
|
362 | (3) |
|
Example: Avoid character escaping in GHCi |
|
|
365 | (3) |
|
|
368 | (2) |
|
|
370 | (2) |
|
11.4 Generalized algebraic data types |
|
|
372 | (6) |
|
Example: Representing dynamically typed values with GADTs |
|
|
373 | (3) |
|
Example: Representing arithmetic expressions with GADTs |
|
|
376 | (2) |
|
11.5 Arbitrary-rank polymorphism |
|
|
378 | (5) |
|
|
378 | (2) |
|
|
380 | (3) |
|
11.6 Advice on dealing with type errors |
|
|
383 | (4) |
|
|
383 | (2) |
|
|
385 | (1) |
|
|
386 | (1) |
|
12 Metaprogramming In Haskell |
|
|
387 | (48) |
|
|
388 | (13) |
|
Basic deriving strategies |
|
|
388 | (5) |
|
The problem of type safety and generalized newtype deriving |
|
|
393 | (7) |
|
Deriving by an example with Deriving Via |
|
|
400 | (1) |
|
12.2 Data-type-generic programming |
|
|
401 | (9) |
|
Generic data-type representation |
|
|
402 | (3) |
|
Example: Generating SQL queries |
|
|
405 | (5) |
|
12.3 Template Haskell and quasiquotes |
|
|
410 | (25) |
|
A tutorial on Template Haskell |
|
|
411 | (10) |
|
Example: Generating remote function calls |
|
|
421 | (14) |
|
|
435 | (42) |
|
13.1 Types for specifying a web API |
|
|
436 | (11) |
|
Implementing a web API from scratch |
|
|
436 | (9) |
|
Implementing a web service with servant |
|
|
445 | (2) |
|
13.2 Toward dependent types with singletons |
|
|
447 | (30) |
|
Safety in Haskell programs |
|
|
447 | (1) |
|
Example: Unsafe interface for elevators |
|
|
448 | (4) |
|
Dependent types and substituting them with singletons |
|
|
452 | (6) |
|
Example: Safe interface for elevators |
|
|
458 | (19) |
|
|
477 | (126) |
|
14 Data-Processing Pipelines |
|
|
479 | (51) |
|
|
480 | (22) |
|
General components and naive implementation |
|
|
481 | (12) |
|
|
493 | (9) |
|
14.2 Approaching an implementation of pipeline stages |
|
|
502 | (15) |
|
Reading and writing data efficiently |
|
|
502 | (2) |
|
Parsing data with parser combinators |
|
|
504 | (5) |
|
Accessing data with lenses |
|
|
509 | (8) |
|
14.3 Example: Processing covid-19 data |
|
|
517 | (13) |
|
|
517 | (2) |
|
|
519 | (8) |
|
|
527 | (3) |
|
15 Working With Relational Databases |
|
|
530 | (73) |
|
15.1 Setting up an example |
|
|
531 | (6) |
|
|
531 | (2) |
|
|
533 | (2) |
|
Data representation in Haskell |
|
|
535 | (2) |
|
15.2 Haskell database connectivity |
|
|
537 | (7) |
|
|
537 | (1) |
|
Relating Haskell data types to database types |
|
|
538 | (1) |
|
Constructing and executing SELECT queries |
|
|
539 | (2) |
|
Manipulating data in a database |
|
|
541 | (1) |
|
Solving tasks by issuing many queries |
|
|
542 | (2) |
|
15.3 The postgresql-simple library |
|
|
544 | (3) |
|
|
544 | (1) |
|
Relating Haskell data types to database types |
|
|
544 | (1) |
|
|
545 | (2) |
|
|
547 | (9) |
|
Structuring programs with hasql |
|
|
547 | (1) |
|
Constructing type-safe SQL statements |
|
|
548 | (4) |
|
Implementing database sessions |
|
|
552 | (1) |
|
Running database sessions |
|
|
553 | (1) |
|
The need for low-level operations and decoding data manually |
|
|
554 | (2) |
|
15.5 Generating SQL with opaleye |
|
|
556 | (12) |
|
Structuring programs with opaleye |
|
|
556 | (1) |
|
Describing database tables and their fields |
|
|
557 | (5) |
|
|
562 | (2) |
|
|
564 | (3) |
|
|
567 | (1) |
|
16.1 Running computations concurrently |
|
|
568 | (16) |
|
An implementation of concurrency in GHC |
|
|
568 | (1) |
|
Low-level concurrency with threads |
|
|
569 | (8) |
|
High-level concurrency with the async package |
|
|
577 | (7) |
|
16.2 Synchronization and communication |
|
|
584 | (19) |
|
Synchronized mutable variables and channels |
|
|
585 | (7) |
|
Software transactional memory (STM) |
|
|
592 | (11) |
Appendix Further reading |
|
603 | (2) |
Index |
|
605 | |