You are previewing Real World Haskell.

Real World Haskell

Cover of Real World Haskell by John Goerzen... Published by O'Reilly Media, Inc.
  1. Real World Haskell
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. A Note Regarding Supplemental Files
    3. Preface
      1. Have We Got a Deal for You!
      2. What to Expect from This Book
      3. What to Expect from Haskell
      4. A Brief Sketch of Haskell’s History
      5. Helpful Resources
      6. Conventions Used in This Book
      7. Using Code Examples
      8. Safari® Books Online
      9. How to Contact Us
      10. Acknowledgments
    4. 1. Getting Started
      1. Your Haskell Environment
      2. Getting Started with ghci, the Interpreter
      3. Basic Interaction: Using ghci as a Calculator
      4. Command-Line Editing in ghci
      5. Lists
      6. Strings and Characters
      7. First Steps with Types
      8. A Simple Program
    5. 2. Types and Functions
      1. Why Care About Types?
      2. Haskell’s Type System
      3. What to Expect from the Type System
      4. Some Common Basic Types
      5. Function Application
      6. Useful Composite Data Types: Lists and Tuples
      7. Functions over Lists and Tuples
      8. Function Types and Purity
      9. Haskell Source Files, and Writing Simple Functions
      10. Understanding Evaluation by Example
      11. Polymorphism in Haskell
      12. The Type of a Function of More Than One Argument
      13. Why the Fuss over Purity?
      14. Conclusion
    6. 3. Defining Types, Streamlining Functions
      1. Defining a New Data Type
      2. Type Synonyms
      3. Algebraic Data Types
      4. Pattern Matching
      5. Record Syntax
      6. Parameterized Types
      7. Recursive Types
      8. Reporting Errors
      9. Introducing Local Variables
      10. The Offside Rule and Whitespace in an Expression
      11. The case Expression
      12. Common Beginner Mistakes with Patterns
      13. Conditional Evaluation with Guards
    7. 4. Functional Programming
      1. Thinking in Haskell
      2. A Simple Command-Line Framework
      3. Warming Up: Portably Splitting Lines of Text
      4. Infix Functions
      5. Working with Lists
      6. How to Think About Loops
      7. Anonymous (lambda) Functions
      8. Partial Function Application and Currying
      9. As-patterns
      10. Code Reuse Through Composition
      11. Tips for Writing Readable Code
      12. Space Leaks and Strict Evaluation
    8. 5. Writing a Library: Working with JSON Data
      1. A Whirlwind Tour of JSON
      2. Representing JSON Data in Haskell
      3. The Anatomy of a Haskell Module
      4. Compiling Haskell Source
      5. Generating a Haskell Program and Importing Modules
      6. Printing JSON Data
      7. Type Inference Is a Double-Edged Sword
      8. A More General Look at Rendering
      9. Developing Haskell Code Without Going Nuts
      10. Pretty Printing a String
      11. Arrays and Objects, and the Module Header
      12. Writing a Module Header
      13. Fleshing Out the Pretty-Printing Library
      14. Creating a Package
      15. Practical Pointers and Further Reading
    9. 6. Using Typeclasses
      1. The Need for Typeclasses
      2. What Are Typeclasses?
      3. Declaring Typeclass Instances
      4. Important Built-in Typeclasses
      5. Automatic Derivation
      6. Typeclasses at Work: Making JSON Easier to Use
      7. Living in an Open World
      8. How to Give a Type a New Identity
      9. JSON Typeclasses Without Overlapping Instances
      10. The Dreaded Monomorphism Restriction
      11. Conclusion
    10. 7. I/O
      1. Classic I/O in Haskell
      2. Working with Files and Handles
      3. Extended Example: Functional I/O and Temporary Files
      4. Lazy I/O
      5. The IO Monad
      6. Is Haskell Really Imperative?
      7. Side Effects with Lazy I/O
      8. Buffering
      9. Reading Command-Line Arguments
      10. Environment Variables
    11. 8. Efficient File Processing, Regular Expressions, and Filename Matching
      1. Efficient File Processing
      2. Filename Matching
      3. Regular Expressions in Haskell
      4. More About Regular Expressions
      5. Translating a glob Pattern into a Regular Expression
      6. An important Aside: Writing Lazy Functions
      7. Making Use of Our Pattern Matcher
      8. Handling Errors Through API Design
      9. Putting Our Code to Work
    12. 9. I/O Case Study: A Library for Searching the Filesystem
      1. The find Command
      2. Starting Simple: Recursively Listing a Directory
      3. A Naive Finding Function
      4. Predicates: From Poverty to Riches, While Remaining Pure
      5. Sizing a File Safely
      6. A Domain-Specific Language for Predicates
      7. Controlling Traversal
      8. Density, Readability, and the Learning Process
      9. Another Way of Looking at Traversal
      10. Useful Coding Guidelines
    13. 10. Code Case Study: Parsing a Binary Data Format
      1. Grayscale Files
      2. Parsing a Raw PGM File
      3. Getting Rid of Boilerplate Code
      4. Implicit State
      5. Introducing Functors
      6. Writing a Functor Instance for Parse
      7. Using Functors for Parsing
      8. Rewriting Our PGM Parser
      9. Future Directions
    14. 11. Testing and Quality Assurance
      1. QuickCheck: Type-Based Testing
      2. Testing Case Study: Specifying a Pretty Printer
      3. Measuring Test Coverage with HPC
    15. 12. Barcode Recognition
      1. A Little Bit About Barcodes
      2. Introducing Arrays
      3. Encoding an EAN-13 Barcode
      4. Constraints on Our Decoder
      5. Divide and Conquer
      6. Turning a Color Image into Something Tractable
      7. What Have We Done to Our Image?
      8. Finding Matching Digits
      9. Life Without Arrays or Hash Tables
      10. Turning Digit Soup into an Answer
      11. Working with Row Data
      12. Pulling It All Together
      13. A Few Comments on Development Style
    16. 13. Data Structures
      1. Association Lists
      2. Maps
      3. Functions Are Data, Too
      4. Extended Example: /etc/passwd
      5. Extended Example: Numeric Types
      6. Taking Advantage of Functions as Data
      7. General-Purpose Sequences
    17. 14. Monads
      1. Revisiting Earlier Code Examples
      2. Looking for Shared Patterns
      3. The Monad Typeclass
      4. And Now, a Jargon Moment
      5. Using a New Monad: Show Your Work!
      6. Mixing Pure and Monadic Code
      7. Putting a Few Misconceptions to Rest
      8. Building the Logger Monad
      9. The Maybe Monad
      10. The List Monad
      11. Desugaring of do Blocks
      12. The State Monad
      13. Monads and Functors
      14. The Monad Laws and Good Coding Style
    18. 15. Programming with Monads
      1. Golfing Practice: Association Lists
      2. Generalized Lifting
      3. Looking for Alternatives
      4. Adventures in Hiding the Plumbing
      5. Separating Interface from Implementation
      6. The Reader Monad
      7. A Return to Automated Deriving
      8. Hiding the IO Monad
    19. 16. Using Parsec
      1. First Steps with Parsec: Simple CSV Parsing
      2. The sepBy and endBy Combinators
      3. Choices and Errors
      4. Extended Example: Full CSV Parser
      5. Parsec and MonadPlus
      6. Parsing a URL-Encoded Query String
      7. Supplanting Regular Expressions for Casual Parsing
      8. Parsing Without Variables
      9. Applicative Functors for Parsing
      10. Applicative Parsing by Example
      11. Parsing JSON Data
      12. Parsing a HTTP Request
    20. 17. Interfacing with C: The FFI
      1. Foreign Language Bindings: The Basics
      2. Regular Expressions for Haskell: A Binding for PCRE
      3. Passing String Data Between Haskell and C
      4. Matching on Strings
    21. 18. Monad Transformers
      1. Motivation: Boilerplate Avoidance
      2. A Simple Monad Transformer Example
      3. Common Patterns in Monads and Monad Transformers
      4. Stacking Multiple Monad Transformers
      5. Moving Down the Stack
      6. Understanding Monad Transformers by Building One
      7. Transformer Stacking Order Is Important
      8. Putting Monads and Monad Transformers into Perspective
    22. 19. Error Handling
      1. Error Handling with Data Types
      2. Exceptions
      3. Error Handling in Monads
    23. 20. Systems Programming in Haskell
      1. Running External Programs
      2. Directory and File Information
      3. Program Termination
      4. Dates and Times
      5. Extended Example: Piping
    24. 21. Using Databases
      1. Overview of HDBC
      2. Installing HDBC and Drivers
      3. Connecting to Databases
      4. Transactions
      5. Simple Queries
      6. SqlValue
      7. Query Parameters
      8. Prepared Statements
      9. Reading Results
      10. Database Metadata
      11. Error Handling
    25. 22. Extended Example: Web Client Programming
      1. Basic Types
      2. The Database
      3. The Parser
      4. Downloading
      5. Main Program
    26. 23. GUI Programming with gtk2hs
      1. Installing gtk2hs
      2. Overview of the GTK+ Stack
      3. User Interface Design with Glade
      4. Event-Driven Programming
      5. Initializing the GUI
      6. The Add Podcast Window
      7. Long-Running Tasks
      8. Using Cabal
    27. 24. Concurrent and Multicore Programming
      1. Defining Concurrency and Parallelism
      2. Concurrent Programming with Threads
      3. Simple Communication Between Threads
      4. The Main Thread and Waiting for Other Threads
      5. Communicating over Channels
      6. Useful Things to Know About
      7. Shared-State Concurrency Is Still Hard
      8. Using Multiple Cores with GHC
      9. Parallel Programming in Haskell
      10. Parallel Strategies and MapReduce
    28. 25. Profiling and Optimization
      1. Profiling Haskell Programs
      2. Controlling Evaluation
      3. Understanding Core
      4. Advanced Techniques: Fusion
    29. 26. Advanced Library Design: Building a Bloom Filter
      1. Introducing the Bloom Filter
      2. Use Cases and Package Layout
      3. Basic Design
      4. The ST Monad
      5. Designing an API for Qualified Import
      6. Creating a Mutable Bloom Filter
      7. The Immutable API
      8. Creating a Friendly Interface
      9. Creating a Cabal Package
      10. Testing with QuickCheck
      11. Performance Analysis and Tuning
    30. 27. Sockets and Syslog
      1. Basic Networking
      2. Communicating with UDP
      3. Communicating with TCP
    31. 28. Software Transactional Memory
      1. The Basics
      2. Some Simple Examples
      3. STM and Safety
      4. Retrying a Transaction
      5. Choosing Between Alternatives
      6. I/O and STM
      7. Communication Between Threads
      8. A Concurrent Web Link Checker
      9. Practical Aspects of STM
    32. A. Installing GHC and Haskell Libraries
      1. Installing GHC
      2. Installing Haskell Software
    33. B. Characters, Strings, and Escaping Rules
      1. Writing Character and String Literals
      2. International Language Support
      3. Escaping Text
    34. Index
    35. About the Authors
    36. Colophon
    37. SPECIAL OFFER: Upgrade this ebook with O’Reilly

Space Leaks and Strict Evaluation

The foldl function that we discussed earlier is not the only place where space leaks can happen in Haskell code. We will use it to illustrate how nonstrict evaluation can sometimes be problematic and how to solve the difficulties that can arise.

Do you need to know all of this right now?

It is perfectly reasonable to skip this section until you encounter a space leak in the wild. Provided you use foldr if you are generating a list, and foldl' instead of foldl otherwise, space leaks are unlikely to bother you in practice for a while.

Avoiding Space Leaks with seq

We refer to an expression that is not evaluated lazily as strict, so foldl' is a strict left fold. It bypasses Haskell’s usual nonstrict evaluation through the use of a special function named seq:

-- file: ch04/Fold.hs
foldl' _    zero []     = zero
foldl' step zero (x:xs) =
    let new = step zero x
    in  new `seq` foldl' step new xs

This seq function has a peculiar type, hinting that it is not playing by the usual rules:

ghci> :type seq
seq :: a -> t -> t

It operates as follows: when a seq expression is evaluated, it forces its first argument to be evaluated, and then returns its second argument. It doesn’t actually do anything with the first argument. seq exists solely as a way to force that value to be evaluated. Let’s walk through a brief application to see what happens:

-- file: ch04/Fold.hs
foldl' (+) 1 (2:[])

This expands as follows:

-- file: ch04/Fold.hs
let new = 1 + 2
in new `seq` foldl' (+) new []

The use of seq forcibly evaluates new to 3 and returns its second argument:

-- file: ch04/Fold.hs
foldl' (+) 3 []

We end up with the following result:

-- file: ch04/Fold.hs

Thanks to seq, there are no thunks in sight.

Learning to Use seq

Without some direction, there is an element of mystery to using seq effectively. Here are some useful rules for using it well.

To have any effect, a seq expression must be the first thing evaluated in an expression:

-- file: ch04/Fold.hs
-- incorrect: seq is hidden by the application of someFunc
-- since someFunc will be evaluated first, seq may occur too late
hiddenInside x y = someFunc (x `seq` y)

-- incorrect: a variation of the above mistake
hiddenByLet x y z = let a = x `seq` someFunc y
                    in anotherFunc a z

-- correct: seq will be evaluated first, forcing evaluation of x
onTheOutside x y = x `seq` someFunc y

To strictly evaluate several values, chain applications of seq together:

-- file: ch04/Fold.hs
chained x y z = x `seq` y `seq` someFunc z

A common mistake is to try to use seq with two unrelated expressions:

-- file: ch04/Fold.hs
badExpression step zero (x:xs) =
    seq (step zero x)
        (badExpression step (step zero x) xs)

Here, the apparent intention is to evaluate step zero x strictly. Since the expression is duplicated in the body of the function, strictly evaluating the first instance of it will have no effect on the second. The use of let from the definition of foldl' just shows illustrates how to achieve this effect correctly.

When evaluating an expression, seq stops as soon as it reaches a constructor. For simple types such as numbers, this means that it will evaluate them completely. Algebraic data types are a different story. Consider the value (1+2):(3+4):[]. If we apply seq to this, it will evaluate the (1+2) thunk. Since it will stop when it reaches the first (:) constructor, it will have no effect on the second thunk. The same is true for tuples: seq ((1+2),(3+4)) True will do nothing to the thunks inside the pair, since it immediately hits the pair’s constructor.

If necessary, we can use normal functional programming techniques to work around these limitations:

-- file: ch04/Fold.hs
strictPair (a,b) = a `seq` b `seq` (a,b)

strictList (x:xs) = x `seq` x : strictList xs
strictList []     = []

It is important to understand that seq isn’t free: it has to perform a check at runtime to see if an expression has been evaluated. Use it sparingly. For instance, while our strictPair function evaluates the contents of a pair up to the first constructor, it adds the overheads of pattern matching, two applications of seq, and the construction of a new tuple. If we were to measure its performance in the inner loop of a benchmark, we might find that it slows down the program.

Aside from its performance cost if overused, seq is not a miracle cure-all for memory consumption problems. Just because you can evaluate something strictly doesn’t mean you should. Careless use of seq may do nothing at all, move existing space leaks around, or introduce new leaks.

The best guides to whether seq is necessary, and how well it is working, are performance measurement and profiling, which we will cover in Chapter 25. From a base of empirical measurement, you will develop a reliable sense of when seq is most useful.

The best content for your career. Discover unlimited learning on demand for around $1/day.