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

Basic Interaction: Using ghci as a Calculator

In addition to providing a convenient interface for testing code fragments, ghci can function as a readily accessible desktop calculator. We can easily express any calculator operation in ghci and, as an added bonus, we can add more complex operations as we become more familiar with Haskell. Even using the interpreter in this simple way can help us to become more comfortable with how Haskell works.

Simple Arithmetic

We can immediately start entering expressions, in order to see what ghci will do with them. Basic arithmetic works similarly to languages such as C and Python—we write expressions in infix form, where an operator appears between its operands:

ghci> 2 + 2
ghci> 31337 * 101
ghci> 7.0 / 2.0

The infix style of writing an expression is just a convenience; we can also write an expression in prefix form, where the operator precedes its arguments. To do this, we must enclose the operator in parentheses:

ghci> 2 + 2
ghci> (+) 2 2

As these expressions imply, Haskell has a notion of integers and floating-point numbers. Integers can be arbitrarily large. Here, (^) provides integer exponentiation:

ghci> 313 ^ 15

An Arithmetic Quirk: Writing Negative Numbers

Haskell presents us with one peculiarity in how we must write numbers: it’s often necessary to enclose a negative number in parentheses. This affects us as soon as we move beyond the simplest expressions.

We’ll start by writing a negative number:

ghci> -3

The - used in the preceding code is a unary operator. In other words, we didn’t write the single number -3; we wrote the number 3 and applied the operator - to it. The - operator is Haskell’s only unary operator, and we cannot mix it with infix operators:

ghci> 2 + -3

    precedence parsing error
        cannot mix `(+)' [infixl 6] and prefix `-' [infixl 6] in the same infix 

If we want to use the unary minus near an infix operator, we must wrap the expression that it applies to in parentheses:

ghci> 2 + (-3)
ghci> 3 + (-(13 * 37))

This avoids a parsing ambiguity. When we apply a function in Haskell, we write the name of the function, followed by its argument—for example, f 3. If we did not need to wrap a negative number in parentheses, we would have two profoundly different ways to read f-3: it could be either “apply the function f to the number -3,” or “subtract the number 3 from the variable f.”

Most of the time, we can omit whitespace (blank characters such as space and tab) from expressions, and Haskell will parse them as we intended. But not always. Here is an expression that works:

ghci> 2*3

And here is one that seems similar to the previous problematic negative number example, but that results in a different error message:

ghci> 2*-3

<interactive>:1:1: Not in scope: `*-'

Here, the Haskell implementation is reading *- as a single operator. Haskell lets us define new operators (a subject that we will return to later), but we haven’t defined *-. Once again, a few parentheses get us and ghci looking at the expression in the same way:

ghci> 2*(-3)

Compared to other languages, this unusual treatment of negative numbers might seem annoying, but it represents a reasoned trade-off. Haskell lets us define new operators at any time. This is not some kind of esoteric language feature; we will see quite a few user-defined operators in the chapters ahead. The language designers chose to accept a slightly cumbersome syntax for negative numbers in exchange for this expressive power.

Boolean Logic, Operators, and Value Comparisons

The values of Boolean logic in Haskell are True and False. The capitalization of these names is important. The language uses C-influenced operators for working with Boolean values: (&&) is logical and, and (||) is logical or:

ghci> True && False
ghci> False || True

While some programming languages treat the number zero as synonymous with False, Haskell does not, nor does it consider a nonzero value to be True:

ghci> True && 1

    No instance for (Num Bool)
      arising from the literal `1' at <interactive>:1:8
    Possible fix: add an instance declaration for (Num Bool)
    In the second argument of `(&&)', namely `1'
    In the expression: True && 1
    In the definition of `it': it = True && 1

Once again, we are faced with a substantial-looking error message. In brief, it tells us that the Boolean type, Bool, is not a member of the family of numeric types, Num. The error message is rather long because ghci is pointing out the location of the problem and hinting at a possible change we could make that might fix it.

Here is a more detailed breakdown of the error message:

No instance for (Num Bool)

Tells us that ghci is trying to treat the numeric value 1 as having a Bool type, but it cannot

arising from the literal '1'

Indicates that it was our use of the number 1 that caused the problem

In the definition of 'it'

Refers to a ghci shortcut that we will revisit in a few pages

Remain fearless in the face of error messages

We have an important point to make here, which we will repeat throughout the early sections of this book. If you run into problems or error messages that you do not yet understand, don’t panic. Early on, all you have to do is figure out enough to make progress on a problem. As you acquire experience, you will find it easier to understand parts of error messages that initially seem obscure.

The numerous error messages have a purpose: they actually help us write correct code by making us perform some amount of debugging up front, before we ever run a program. If you come from a background of working with more permissive languages, this may come as something of a shock. Bear with us.

Most of Haskell’s comparison operators are similar to those used in C and the many languages it has influenced:

ghci> 1 == 1
ghci> 2 < 3
ghci> 4 >= 3.99

One operator that differs from its C counterpart is is not equal to. In C, this is written as !=. In Haskell, we write (/=), which resembles the ≠ notation used in mathematics:

ghci> 2 /= 3

Also, where C-like languages often use ! for logical negation, Haskell uses the not function:

ghci> not True

Operator Precedence and Associativity

Like written algebra and other programming languages that use infix operators, Haskell has a notion of operator precedence. We can use parentheses to explicitly group parts of an expression, and precedence allows us to omit a few parentheses. For example, the multiplication operator has a higher precedence than the addition operator, so Haskell treats the following two expressions as equivalent:

ghci> 1 + (4 * 4)
ghci> 1 + 4 * 4

Haskell assigns numeric precedence values to operators, with 1 being the lowest precedence and 9 the highest. A higher-precedence operator is applied before a lower-precedence operator. We can use ghci to inspect the precedence levels of individual operators, using ghci’s :info command:

ghci> :info (+)
class (Eq a, Show a) => Num a where
  (+) :: a -> a -> a
  	-- Defined in GHC.Num
infixl 6 +
ghci> :info (*)
class (Eq a, Show a) => Num a where
  (*) :: a -> a -> a
  	-- Defined in GHC.Num
infixl 7 *

The information we seek is in the line infixl 6 +, which indicates that the (+) operator has a precedence of 6. (We will explain the other output in a later chapter.) infixl 7 * tells us that the (*) operator has a precedence of 7. Since (*) has a higher precedence than (+), we can now see why 1 + 4 * 4 is evaluated as 1 + (4 * 4), and not (1 + 4) * 4.

Haskell also defines associativity of operators. This determines whether an expression containing multiple uses of an operator is evaluated from left to right or right to left. The (+) and (*) operators are left associative, which is represented as infixl in the preceding ghci output. A right associative operator is displayed with infixr:

ghci> :info (^)
(^) :: (Num a, Integral b) => a -> b -> a 	-- Defined in GHC.Real
infixr 8 ^

The combination of precedence and associativity rules are usually referred to as fixity rules.

Undefined Values, and Introducing Variables

Haskell’s Prelude, the standard library we mentioned earlier, defines at least one well-known mathematical constant for us:

ghci> pi

But its coverage of mathematical constants is not comprehensive, as we can quickly see. Let us look for Euler’s number, e:

ghci> e

<interactive>:1:0: Not in scope: `e'

Oh well. We have to define it ourselves.

Don’t worry about the error message

If the not in scope error message seems a little daunting, do not worry. All it means is that there is no variable defined with the name e.

Using ghci’s let construct, we can make a temporary definition of e ourselves:

ghci> let e = exp 1

This is an application of the exponential function, exp, and our first example of applying a function in Haskell. While languages such as Python require parentheses around the arguments to a function, Haskell does not.

With e defined, we can now use it in arithmetic expressions. The (^) exponentiation operator that we introduced earlier can only raise a number to an integer power. To use a floating-point number as the exponent, we use the(**) exponentiation operator:

ghci> (e ** pi) - pi

This syntax is ghci-specific

The syntax for let that ghci accepts is not the same as we would use at the top level of a normal Haskell program. We will see the normal syntax in Introducing Local Variables.

Dealing with Precedence and Associativity Rules

It is sometimes better to leave at least some parentheses in place, even when Haskell allows us to omit them. Their presence can help future readers (including ourselves) to understand what we intended.

Even more importantly, complex expressions that rely completely on operator precedence are notorious sources of bugs. A compiler and a human can easily end up with different notions of what even a short, parenthesis-free expression is supposed to do.

There is no need to remember all of the precedence and associativity rules numbers: it is simpler to add parentheses if you are unsure.

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