haskell book sample

73

Upload: zachery-gao

Post on 09-Dec-2015

267 views

Category:

Documents


9 download

DESCRIPTION

Haskell Book Sample

TRANSCRIPT

Page 1: Haskell Book Sample
Page 2: Haskell Book Sample

2

0.1 Hello reader!

This is a sample of our third chapter on strings and printing. We’ve alsoincluded part of the front matter in the hopes that will explain some ofhow we’re approaching teaching Haskell. We hope it gives you some ideaas to what our “style” is like, although there might be some stuff thatmay not make sense without the rest of the introduction. If you have anyquestions, please see the book website at http://haskellbook.com forsupport information.

When to expect a release

We will do a release approximately once every month.

What we’ve got so far and the table of contents

Chapters available

1. Front matter

2. Lambda Calculus

3. Hello Haskell

4. Strings

5. Basic Datatypes

6. Types

7. Typeclasses

8. Functions

9. Recursion

10. Lists

11. Folds

Page 3: Haskell Book Sample

3

12. Algebraic datatypes

13. (Surprise chapter)

14. Building projects in Haskell

On deck

Defined as being >50% completed

15. Testing

16. Monoid, Semigroup

17. Functors

18. Applicative

19. Monad (SPOOKY? No.)

20. Foldable/Traversable

21. Reader/State

Still being written

22. Parsers

23. Monad Transformers

24. Non-strictness and efficiency

25. Commonly used data structures

26. (Four final chapters TBA)

Does the material we cover sound like what the doctor ordered? Head toour gumroad page at https://gumroad.com/l/haskellbook to get in ourearly access period.

Page 4: Haskell Book Sample

4

0.2 Introduction

Welcome to a new way to learn Haskell. Perhaps you are coming to this bookfrustrated by previous attempts to learn Haskell. Perhaps you have only thefaintest notion of what Haskell is. Perhaps you are coming here becauseyou are not convinced that anything will ever be better than CommonLisp/Scala/Ruby/whatever language you love, and you want to argue withus. Perhaps you were just looking for the 18 billionth (n.b.: this numbermay be inaccurate) monad tutorial, certain that this time around you willunderstand monads once and for all. Whatever your situation, welcome andread on! It is our goal here to make Haskell as clear, painless, and practicalas we can, no matter what prior experiences you’re bringing to the table.

Why Haskell

If you are new to programming entirely, Haskell is a great first language.You may have noticed the trend of “Functional Programming in [ImperativeLanguage]” books and tutorials and learning Haskell gets right to the heartof what functional programming is. Languages such as Java are graduallyadopting functional concepts, but most such languages were not designedto be functional languages. We would not encourage you to learn Haskellas an only language, but because Haskell is a pure functional language, itis a fertile environment for mastering functional programming. That wayof thinking and problem solving is useful, no matter what other languagesyou might know or learn.

If you are already a programmer, writing Haskell code may seem to be moredifficult up front, not just because of the hassle of learning a language that issyntactically and conceptually different from a language you already know,but also because of features such as strong typing that enforce some disci-pline in how you write your code. Without wanting to trivialize the learningcurve, we would argue that part of being a professional means acceptingthat your field will change as new information and research demands it.

It also means confronting human frailty. In software this means that wecannot track all relevant metadata about our programs in our heads our-selves. We have limited space for our working memory, and using it up for

Page 5: Haskell Book Sample

5

anything a computer can do for us is counter-productive. We don’t writeHaskell because we’re geniuses – we use tools like Haskell because we’re notgeniuses and it helps us. Good tools like Haskell enable us to work faster,make fewer mistakes, and have more information about what our code issupposed to do as we read it.

We use Haskell because it is easier (over the long run) and enables us to do abetter job. That’s it. There’s a ramp-up required in order to get started, butthat can be ameliorated with patience and a willingness to work throughexercises.

What is Haskell for?

Haskell is a general purpose, functional programming language. It’s notscoped to a particular problem set. It’s applicable virtually anywhere onewould use a program to solve a problem, save for some specific embeddedapplications. If you could write software to solve a problem, you couldprobably use Haskell.

Haskell’s ecosystem has an array of well-developed libraries and tools. Hoogleis a search tool that allows you to search for the function you want by typesignature. It has a sophisticated structural understanding of types and canmatch on more than just syntax. Libraries such as Yesod and Scotty allowyou to build web applications quickly, each addressing a different niche ofweb development. Aeson is popular and in wide use in the Haskell commu-nity for processing JSON data which is currently the lingua franca for dataserialization and transmission on the web. Gloss is popular for rendering 2dvector graphics. Xmonad is a tiled window manager for Linux/X11, writtenin Haskell and popular with Haskell users and non-Haskellers. Many morepeople use pandoc which is as close to a universal document conversion andprocessing tool as currently exists, and is also written in Haskell.

Haskell is used in industrial settings like oil & gas control systems, dataanonymization, mobile applications for iOS and Android, embedded soft-ware development, REST APIs, data processing at scale, and desktop appli-cations. Entire financial front offices have used Haskell to replace everythingfrom C++ trading applications to apps running in Excel, and even some in-die game developers are using Haskell with functional reactive programming

Page 6: Haskell Book Sample

6

libraries to make their projects.

OK, but I was just looking for a monad tutorial...

We encourage you to forget what you might already know about program-ming and come at this course in Haskell with a beginner’s mindset. Makeyourself an empty vessel, ready to let the types flow through you.

If you are an experienced programmer, learning Haskell is more like learningto program all over again. Getting somebody from Python to JavaScriptis comparatively trivial. They share very similar semantics, structure, typesystem (none, for all intents and purposes), and problem-solving patterns(e.g., representing everything as maps, loops, etc.). Haskell, for most whoare already comfortable with an imperative or untyped programming lan-guage, will impose previously unknown problem-solving processes on thelearner. This makes it harder to learn not because it is intrinsically harder,but because most people who have learned at least a couple of programminglanguages are accustomed to the process being trivial, and their expecta-tions have been set in a way that lends itself to burnout and failure.

If you are learning Haskell as a first language, you may have experienced aspecific problem with the existing Haskell materials and explanations: theyassume a certain level of background with programming, so they frequentlyexplain Haskell concepts in terms, by analogy or by contrast, of program-ming concepts from other languages. This is obviously confusing for thestudent who doesn’t know those other languages, but we posit that it isjust as unhelpful for experienced programmers. Most attempts to compareHaskell with other languages only lead to a superficial understanding of theHaskell concepts, and making analogies to loops and other such constructscan lead to bad intuitions about how Haskell code works. For all of thesereasons, we have tried to avoid relying on knowledge of other programminglanguages. Just as you can’t achieve fluency in a human language so long asyou are still attempting direct translations of concepts and structures fromyour native language to the target language, it’s best to learn to understandHaskell on its own terms.

This book is not something you want to just leaf through the first timeyou read it. It is more of a course than a book, something to be worked

Page 7: Haskell Book Sample

7

through. There are exercises sprinkled liberally throughout the book; weencourage you to do all of them, even when they seem simple. Thoseexercises are where the majority of your epiphanies will come from. Noamount of chattering, no matter how well structured and suited to yourtemperament, will be as effective as doing the work. We do not recommendthat you pass from one section of the book to the next without doing atleast a few of the exercises. If you do get to a later chapter and find you didnot understand a concept or structure well enough, you can always returnto an earlier chapter and do more exercises until you understand it. TheFreenode IRC channel #haskell-beginners has teachers who will be gladto help you as well, and they especially welcome questions regarding specificproblems that you are trying to solve.1

We believe that spaced repetition and iterative deepening are effective strate-gies for learning, and the structure of the book reflects this. You may noticewe mention something only briefly at first, then return to it over and over.As your experience with Haskell deepens, you have a base from which tomove to a deeper level of understanding. Try not to worry that you don’tunderstand something completely the first time we mention it. By movingthrough the exercises and returning to concepts, you can develop a solidintuition for the functional style of programming.

The exercises in the first few chapters are designed to rapidly familiarizeyou with basic Haskell syntax and type signatures, but you should expectexercises to grow more challenging in each successive chapter. Where possi-ble, reason through the code samples and exercises in your head first, thentype them out – either into the REPL or into a source file – and check tosee if you were right. This will maximize your ability to understand andreason about Haskell. Later exercises may be difficult. If you get stuck onan exercise for an extended period of time, proceed and return to it at alater date.

You are not a Spartan warrior – you do not need to come back either withyour shield or on it. Returning later to investigate things more deeply is an

1Freenode IRC (Internet Relay Chat) is a network of channels for textual chat. Thereare other IRC networks around, as well as other group chat platforms, but the FreenodeIRC channels for Haskell are popular meeting places for the Haskell community. Thereare several ways to access Freenode IRC if you’re interested in getting to know thecommunity in their natural habitat.

Page 8: Haskell Book Sample

8

efficient technique, not a failure.

What’s in this book?

We cover a mix of practical and abstract matters required to use Haskellfor a wide variety of projects. Chris’s experience is principally with pro-duction backend systems and frontend web applications. Julie is a linguistand teacher by training and education, and learning Haskell was her firstexperience with computer programming. The educational priorities of thisbook are biased by those experiences. Our goal is to help you not just writetypesafe functional code but to understand it on a deep enough level thatyou can go from here to more advanced Haskell projects in a variety of ways,depending on your own interests and priorities.

One of the most common responses we hear from people who have beentrying to learn Haskell is, “OK, I’ve worked through this book, but I stilldon’t know how to actually write anything of my own.” We’ve combinedpractical exercises with principled explanations in order to alleviate thatproblem.

Each chapter focuses on different aspects of a particular topic. We startwith a short introduction to the lambda calculus. What does this have todo with programming? All modern functional languages are based on thelambda calculus, and a passing familiarity with it will help you down theroad with Haskell. If you’ve understood the lambda calculus, understandingthe feature of Haskell known as “currying” will be a breeze, for example.

The next few chapters cover basic expressions and functions in Haskell, somesimple operations with strings (text), and a few essential types. You mayfeel a strong temptation, especially if you have programmed previously, toglance at those chapters, decide they are too easy and skip them. Please donot do this. Even if those first chapters are covering concepts you’re famil-iar with, it’s important to spend time getting comfortable with Haskell’srather terse syntax, making sure you understand the difference betweenworking in the REPL and working in source files, and becoming familiarwith the compiler’s sometimes quirky error messages. Certainly you maywork quickly through those chapters – just don’t skip them.

Page 9: Haskell Book Sample

9

From there, we build both outward and upward so that your understandingof Haskell both broadens and deepens. When you finish this book, you willnot just know what monads are, you will know how to use them effectivelyin your own programs and understand the underlying algebra involved. Wepromise – you will. We only ask that you do not go on to write a monadtutorial on your blog that explains how monads are really just like jalapenopoppers.

In each chapter you can expect:

• additions to your vocabulary of standard functions;

• syntactic patterns that build on each other;

• theoretical foundations so you understand how Haskell works;

• illustrative examples of how to read Haskell code;

• step-by-step demonstrations of how to write your own functions;

• explanations of how to read common error messages and how to avoidthose errors;

• exercises of varying difficulty sprinkled throughout;

• definitions of important terms.

We have put the definitions at the end of each chapter. Each term is,of course, defined within the body of the chapter, but we added separatedefinitions at the end as a point of review. If you’ve taken some time offbetween one chapter and the next, the definitions can remind you of whatyou have already learned, and, of course, they may be referred to any timeyou need a refresher.

A few words about working environments

This book does not offer much instruction on using the terminal and texteditor. The instructions provided assume you know how to find your wayaround your terminal and understand how to do simple tasks like make a

Page 10: Haskell Book Sample

10

directory or open a file. Due to the number of text editors available, we donot provide specific instructions for any of them.

If you are new to programming and don’t know what to use, consider usingan editor that is unobtrusive, uses the Common User Access bindings mostcomputer users are accustomed to, and doesn’t force you to learn muchthat is new (assuming you’re already used to using commands like ctrl-cto copy), such as Gedit or Sublime Text. When you’re initially learning toprogram, it’s better to focus on learning Haskell rather than struggling witha new text editor at the same time. However, if you believe programmingwill be a long-term occupuation or preoccupation of yours, you may wantto move to text editors that can grow with you over time such as Emacs orVim.

Page 11: Haskell Book Sample

11

0.3 A few words about the examples and ex-ercises

We have tried to include a variety of examples and exercises in each chapter.While we have made every effort to include only exercises that serve a clearpedagogical purpose, we recognize that not all individuals enjoy or learnas much from every type of demonstration or exercise. Also, since ourreaders will necessarily come to the book with different backgrounds, someexercises may seem too easy or difficult to you but be just right for someoneelse. Do your best to work through as many exercises as seems practical foryou. But if you skip all the types and typeclasses exercises and then findyourself confused when we get to Monoid, by all means, come back and domore exercises until you understand.

Here are a few things to keep in mind to get the most out of them:

• Examples are usually designed to demonstrate, with real code, whatwe’ve just talked or are about to talk about in further detail.

• You are intended to type all of the examples into the REPL or a fileand load them. We strongly encourage you to attempt to modify theexample and play with the code after you’ve made it work. Forminghypotheses about what effect changes will have and verifying them iscritical!

• Sometimes the examples are designed intentionally to be broken. Checksurrounding prose if you’re confused by an unexpected error as wewill not show you code that doesn’t work without commenting on thebreakage. If it’s still broken and it’s not supposed to be, you shouldstart checking your syntax for errors.

• Not every example is designed to be entered into the REPL. Not everyexample is designed to be entered into a file. We explain the syntacticdifferences between files and REPL expressions. You are expected toperform the translation between the two yourself after it has beenexplained. This is so you are accustomed to working with code in aninteractive manner by the time you finish the book.

Page 12: Haskell Book Sample

12

• Exercises in the body of the chapter are usually targeted to the infor-mation that was covered in the immediately preceding section(s).

• Exercises at the end of the chapter generally include some reviewquestions covering material from previous chapters and are more orless ordered from easiest to most challenging. Your mileage may vary.

• Even exercises that seem easy can increase your fluency in a topic.We do not fetishize difficulty for difficulty’s sake. We just want youto understand the topics as well as possible. That can mean comingat the same problem from different angles.

• We ask you to write and then rewrite (using different syntax) a lot offunctions. Few problems have only one possible solution, and solvingthe same problem in different ways increases your fluency and comfortwith the way Haskell works (its syntax, its semantics, and in somecases, its evaluation order).

• Do not feel obligated to do all the exercises in a single sitting or even ina first pass through the chapter. In fact, spaced repetition is generallya more effective strategy.

• Some exercises, particularly in the earlier chapters, may seem verycontrived. Well, they are. But they are contrived to pinpoint certainlessons. As the book goes on and you have more Haskell under yourbelt, the exercises become less contrived and more like “real Haskell.”

• It is better to type the code examples and exercises yourself ratherthan copy and paste. Typing it out yourself makes you pay moreattention to it, so you ultimately learn much more from doing so.

• We recommend you move away from typing code examples and ex-ercises directly into GHCi sooner rather than later and develop thehabit of working in source files. It isn’t just because the syntax canbecome unwieldy in the REPL. Editing and modifying code, as youwill be doing a lot as you rework exercises, is easier and more practi-cal in a source file. You will still load your code into the REPL fromthe file to run it, and we’ll cover how to do this in the appropriatechapter.

Page 13: Haskell Book Sample

13

• Another benefit to writing code in a source file and then loading it intothe REPL is that you can write comments about the process you wentthrough in solving a problem. Writing out your own thought processcan clarify your thoughts and make the solving of similar problemseasier. At the very least, you can refer back to your comments andlearn from yourself.

• Sometimes we intentionally underspecify function definitions. You’llcommonly see things like:

f = undefined

Even when f will probably take named arguments in your implemen-tation, we’re not going to name them for you. Nobody will scaffoldyour code for you in your future projects, so don’t expect this bookto either.

Page 14: Haskell Book Sample

Chapter 1

Hello, Haskell!Basic expressions and functions

Functions are beacons ofconstancy in a sea of turmoil.

Mike Hammond

1

Page 15: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 2

1.1 Hello, Haskell

Welcome to your first step in learning Haskell. Before you begin with themain course of this book, you will need to install the tools necessary to workwith the language in order to complete the exercises as you work throughthe book. You can find the installation instructions online at https://github.com/bitemyapp/learnhaskell. If you wish, you can completethe installation and practice only for GHCi now and wait to install Cabal,but you will need Cabal up and running for the chapter about buildingprojects. The rest of this chapter will assume that you have completed theinstallation and are ready to begin working.

In this chapter, you will

• use Haskell code in the interactive environment and also from sourcefiles;

• understand the building blocks of Haskell: expressions and functions;

• learn some features of Haskell syntax and conventions of good Haskellstyle;

• modify simple functions.

1.2 Interacting with Haskell code

Haskell offers two primary ways of writing and compiling code. The firstis inputting it directly into the interactive environment known as GHCi,or the REPL. The second is typing it into a text editor, saving, and thenloading that source file into GHCi. This section offers an introduction toeach method.

Using the REPL

REPL is an acronym short for Read-Eval-Print Loop. REPLs are interac-tive programming environments where you can input code, have it evaluated

Page 16: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 3

by the language implementation, and see the result. They originated withLisp but are now common to modern programming languages includingHaskell.

Assuming you’ve completed your installation, you should be able to openyour terminal or command prompt, enter ghci, hit enter, and see somethinglike the following:

GHCi, version 7.8.3Loading package ghc-prim ... linking ... done.Loading package integer-gmp ... linking ... done.Loading package base ... linking ... done.Prelude>

It might not be exactly the same, depending on your configuration, butit should be very similar. If you are just downloading GHC for the firsttime, you may have the newest release number, which is 7.10.1. The newestversion includes some important changes which we will cover in due time.

Now try entering some simple arithmetic at your prompt:

Prelude> 2 + 24Prelude> 7 < 9TruePrelude> 10 ^ 2100

If you can enter simple equations at the prompt and get the expected results,congratulations – you are now a functional programmer! More to the point,your REPL is working well and you are ready to proceed.

To exit GHCi, use the command :quit or :q.

Abbreviating GHCi commands Throughout the book, we’ll be usingGHCi commands, such as :quit and :info in the REPL. We will presentthem in the text spelled out, but they can generally be abbreviated to just

Page 17: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 4

the colon and the first letter. That is, :quit becomes :q, :info becomes:i and so forth. It’s good to type the word out the first few times youuse it, to help you remember what the abbreviation stands for, but as youremember the commands, we will start abbreviating them and you will, too.

Saving scripts and loading them

As nice as REPLs are, usually you want to store code in a file so you canbuild it incrementally. Almost all nontrivial programming you do will in-volve editing libraries or applications made of nested directories containingfiles with Haskell code in them. The basic process is to have the code andimports (more on that later) in a file, load it into the REPL, and interactwith it there as you’re building, modifying, and testing it.

You’ll need a file named test.hs. The .hs file extension denotes a Haskellsource code file. Depending on your setup and the workflow you’re comfort-able with, you can make a file by that name and then open it in your texteditor or you can open your text editor, open a new file, and then save thefile with that file name.

Then enter the following code into the file and save it:

sayHello :: String -> IO ()sayHello x = putStrLn ("Hello, " ++ x ++ "!")

Here, :: is a way to write down a type signature. You can think if it assaying, “has the type”. So, sayHello has the type String -> IO (). Thesefirst chapters are focused on syntax, so if you don’t understand what typesor type signatures are, that’s okay – we will explain them soon. For now,keep going.

Then in the same directory where you’ve stored your test.hs file, openyour ghci REPL and do the following:

Prelude> :load test.hsPrelude> sayHello "Tina"Hello, Tina!

Page 18: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 5

Prelude>

After using :load to load your test.hs, the sayHello function is visiblein the REPL and you can pass it a string argument, such as “Tina” (notethe quotation marks), and see the output.

Note that we used the command :load to load the file. Special commandsthat only GHCi understands begin with the : character. :load is notHaskell code; it’s just a GHCi feature. We will see more of these commandsthroughout the book.

1.3 Understanding expressions

Everything in Haskell is an expression. Expressions may be values, combi-nations of values, and/or functions applied to values. Expressions evaluateto a result. In the case of a literal value, the evaluation is trivial as it onlyevaluates to itself. In the case of an arithmetic equation, the evaluationprocess is the process of computing the operator and its arguments, as youmight expect. But, even though not all of your programs will be aboutdoing arithmetic, all of Haskell’s expressions work in a similar way, eval-uating to a result in a predictable, transparent manner. Expressions arethe building blocks of our programs, and programs themselves are one bigexpression made of smaller expressions.

The following are all expressions:

11 + 1"Icarus"

Each can be examined in the GHCi REPL where you can enter the code atthe prompt, then hit ‘enter’ to see the result of evaluating the expression.The numeric value 1, for example, has no further reduction step, so it standsfor itself. If you haven’t already, open up your terminal and at the prompt,enter ghci to get your REPL going and start following along with the codeexamples.

Page 19: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 6

When we enter this into GHCi:

Prelude> 11

We see 1 printed because it cannot be reduced any further.

In the next example, GHCi reduces the expression 1 + 2 to 3, then printsthe number 3. The reduction terminates with the value 3 because there areno more terms to evaluate:

Prelude> 1 + 23

Expressions can be nested in numbers limited only by our willingness totake the time to write them down, much like in arithmetic:

Prelude> (1 + 2) * 39Prelude> ((1 + 2) * 3) + 100109

You can keep expanding on this, nesting as many expressions as you’d likeand evaluating them. But, we don’t have to limit ourselves to expressionssuch as these.

Normal form We say that expressions are in normal form when thereare no more evaluation steps that can be taken. Put differently, whenthey’ve reached an irreducible form. The normal form of 1 + 1 is 2. Why?Because the expression 1 + 1 can be evaluated or reduced by applyingthe addition operator to the two arguments. In other words, 1 + 1 is areducible expression, while 2 is an expression but is no longer reducible – itcan’t evaluate into anything other than itself. Reducible expressions are alsocalled redexes. While we will generally refer to this process as evaluationor reduction, you may also hear it called “normalizing” or “executing” anexpression.

Page 20: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 7

1.4 Functions

Expressions are the most basic unit of a Haskell program, and functionsare a specific type of expression. Functions in Haskell are closely related tofunctions in mathematics, which is to say they map an input or set of inputsto an output. A function is an expression that is applied to an argument(or parameter) and always returns a result. Because they are built purelyof expressions, they will always evaluate to the same result when given thesame values.

As in the lambda calculus, all functions in Haskell take one argument andreturn one result. The way to think of this is that, in Haskell, when it seemswe are passing multiple arguments to a function, we are actually applyinga series of nested functions, each to one argument. This is called currying,and it will be addressed in greater detail later.

You may have noticed that the expressions we’ve looked at so far use literalvalues with no variables or abstractions. Functions allow us to abstractthe parts of code we’d want to reuse for different literal values. Instead ofnesting addition expressions, for example, we could write a function thatwould add the value we wanted wherever we called that function.

For example, say you had a bunch of simple expressions you needed tomultiply by 3. You could keep entering them as individual expressions likethis:

Prelude> (1 + 2) * 39Prelude> (4 + 5) * 327Prelude> (10 + 5) * 345

But you don’t want to do that. Functions are how we factor out the patterninto something we can reuse with different inputs. You do that by namingthe function and introducing an independent variable as the argument tothe function. Functions can also appear in the expressions that form the

Page 21: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 8

bodies of other functions or be used as arguments to functions, just as anyother value can be.

In this case, we have a series of expressions that we want to multiply by3. Let’s think in terms of a function: what part is common to all theexpressions? What part varies? We know we have to give functions a nameand apply them to an argument, so what could we call this function andwhat sort of argument might we apply it to?

The common pattern is the * 3 bit. The part that varies is the additionexpression before it, so we will make that a variable. We will name ourfunction and apply it to the variable. When we input a value for the variable,our function will evaluate that, multiply it by 3, and return a result. In thenext section, we will formalize this into a proper Haskell function.

Defining functions

Function definitions all share a few things in common. First, they startwith the name of the function. This is followed by the formal arguments orparameters of the function, separated only by white space. Next there is anequal sign, which, notably, does not imply equality of value. Finally thereis an expression that is the body of the function and can be evaluated toreturn a value.

Defining functions in a normal Haskell source code file and in GHCi are alittle different. To introduce definitions of values or functions in GHCi youmust use let, which looks like this:

Prelude> let triple x = x * 3

In a source file we would enter it like this:

triple x = x * 3

Let’s examine each part of that:

Page 22: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 9

triple x = x * 3-- [1] [2] [3] [ 4 ]

1. Name of the function we are defining. Note that it is lowercase.

2. Argument to our function. The arguments to our function correspondto the “head” of a lambda.

3. The = is used to define values and functions. Reminder: this is nothow we express equality between two values in Haskell.

4. Body of the function, an expression that will be evaluated if the argu-ment x is applied. Here the entire expression x * 3 constitutes thebody of the function.

Capitalization matters! Names of modules and names of types, suchas Integer, start with a capital letter. They can also be CamelCase whenyou want to maintain readability of multiple-word names.

Function names start with lowercase letters. Sometimes for clarity in func-tion names, you may want camelBack style, and that is good style providedthe very first letter remains lowercase.

Variables are lowercase.

Playing with the triple function First, try entering the triple func-tion directly into the REPL using let. Now call the function by name andintroduce a numeric value for the x argument:

Prelude> triple 26

Next, enter the second version (the one without let) into a source file andsave the file. Load it into GHCi, using the :load or :l command. Once it’sloaded, you can call the function at the prompt using the function name,triple, followed by a numeric value, just as you did in the REPL exampleabove. Try using different values for x – integer values or other arithmetic

Page 23: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 10

expressions. Then try changing the function itself in the source file andreloading it to see what changes.

Evaluating functions

Calling the function by name and introducing a value for the x argumentmakes our function a reducible expression. In a pure functional languagelike Haskell, we can replace applications of functions with their definitionsand get the same result, just like in math. As a result when we see:

triple 2

We can know that, since triple is defined as x = x * 3, the expression isequivalent to:

-- add comments heretriple 2

(triple x = x * 3) 2(triple 2 = 2 * 3)2 * 36

What we’ve done here is apply triple to the value 2 and then reduce theexpression to the final result 6. Our expression triple 2 is in canonicalor normal form when it reaches the number 6 because the value 6 has noremaining reducible expressions.

You may notice that after loading code from a source file, the GHCi promptis no longer Prelude>. To return to the Prelude> prompt, use the com-mand :m, which is short for :module. This will unload the file from GHCi,so the code in that file will no longer be in scope in your REPL.

Conventions for variables

Haskell uses a lot of variables, and some conventions have developed. It’snot critical that you memorize this, because for the most part, these are

Page 24: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 11

merely conventions, but familiarizing yourself with them will help you readHaskell code.

Type variables (that is, variables in type signatures) generally start at aand go from there: a, b, c, and so forth. You may occasionally see themwith numbers appended to them, e.g., a1.

Functions can be used as arguments and in that case are typically labeledwith variables starting at f (followed by g and so on). They may sometimeshave numbers appended (e.g., f1) and may also sometimes be decoratedwith the ' character as in f'. This would be pronounced “eff-prime,” shouldyou have need to say it aloud. Usually this denotes a function that is closelyrelated to or a helper function to function f. Functions may also be givenvariable names that are not on this spectrum as a mnemonic. For example,a function that results in a list of prime numbers might be called p, or afunction that fetches some text might be called txt. Variables do not haveto be a single letter, though they often are.

Arguments to functions are most often given names starting at x, againoccasionally seen numbered as in x1. Other single-letter variable namesmay be chosen when they serve a mnemonic role, such as choosing r torepresent a value that is the radius of a circle.

If you have a list of things you have named x, by convention that will usuallybe called xs, that is, the plural of x. You will see this convention often inthe form (x:xs), which means you have a list in which the head of the listis x and the rest of the list is xs.

All of these, though, are merely conventions, not definite rules. While wewill generally adhere to the conventions in this book, any Haskell code yousee out in the wild may not. Calling a type variable x instead of a is notgoing to break anything. As in the lambda calculus, the names don’t haveany inherent meaning. We offer this information as a descriptive guide ofHaskell conventions, not as rules you must follow in your own code.

Intermission: Exercises

1. Given the following lines of code as they might appear in a source file,how would you change them to use them directly in the REPL?

Page 25: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 12

half x = x / 2

square x = x * x

2. Write one function that can accept one argument and work for all thefollowing functions. Be sure to name the function.

3.14 * (5 * 5)3.14 * (10 * 10)3.14 * (2 * 2)3.14 * (4 * 4)

1.5 Infix operators

Functions in Haskell default to prefix syntax, meaning that the function be-ing applied is at the beginning of the expression rather than the middle. Wesaw that with our triple function, and we see it with standard functionssuch as the identity or id function. This function just returns whatevervalue it is given as an argument:

Prelude> id 11

While this is the default syntax for functions, not all functions are prefix.There are a group of operators, such as the arithmetic operators we’ve beenusing, that are indeed functions (they apply to arguments to produce anoutput) but appear by default in an infix position.

Operators are functions which can be used in infix style. All operators arefunctions; not all functions are operators. While triple and id are prefixfunctions (not operators), the + function is an infix operator:

Prelude> 1 + 12

Now we’ll try a few other mathematical operators:

Page 26: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 13

Prelude> 100 + 100200Prelude> 768395 * 2135634516410108716275Prelude> 123123 / 1231001.0Prelude> 476 - 36440Prelude> 10 / 42.5

You can sometimes use functions in an infix or prefix style, with a smallchange in syntax:

Prelude> 10 `div` 42Prelude> div 10 42

Associativity and precedence

As you may remember from your math classes, there’s a default associativityand precedence to the infix operators (*), (+), (-), and (/).

We can ask GHCi for information such as associativity and precedenceof operators and functions by using the :info command. When you askGHCi for the :info about an operator or function, it provides the typeinformation and tells you whether it’s an infix operator with precedence.We will focus on the precedence and associativity for infix operators. Here’swhat the code in Prelude says for (*), (+), and (-) at time of writing:

Page 27: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 14

:info (*)infixl 7 *-- [1] [2] [3]

:info (+) (-)infixl 6 +, --- [4]

1. infixl means infix operator, left associative

2. 7 is the precedence: higher is applied first, on a scale of 0-9.

3. Infix function name: in this case, multiplication

4. We use the comma here to assign left-associativity and precedence 6for two functions (+) and (-)

Here infixl means the operator associates to the left. Let’s play withparentheses and see what this means. Continue to follow along with thecode via the REPL:

-- this2 * 3 * 4

-- is evaluated as if it was(2 * 3) * 4-- Because of left-associativity from infixl

Here’s an example of a right-associative infix operator:

Prelude> :info (^)infixr 8 ^-- [1] [2] [3]

1. infixr means infix operator, right associative

Page 28: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 15

2. 8 is the precedence. Higher precedence, indicated by higher numbers,is applied first, so this is higher precedence than multiplication (7),addition, or subtraction (both 6).

3. Infix function name: in this case, exponentiation.

It was hard to tell before why associativity mattered with multiplication,because multiplication is associative. So shifting the parentheses aroundnever changes the result. Exponentiation, however, is not associative andthus makes a prime candidate for demonstrating left vs. right associativity.

Prelude> 2 ^ 3 ^ 42417851639229258349412352Prelude> 2 ^ (3 ^ 4)2417851639229258349412352Prelude> (2 ^ 3) ^ 44096

As you can see, adding parentheses starting from the right-hand side of theexpression when the operator is right-associative doesn’t change anything.However, if we parenthesize from the left, we get a different result when theexpression is evaluated.

Your intuitions about precedence, associativity, and parenthesization frommath classes will generally hold in Haskell:

2 + 3 * 4

(2 + 3) * 4

What’s the difference between these two? Why are they different?

Intermission: Exercises

Below are some pairs of functions that are alike except for parenthesization.Read them carefully and decide if the parentheses change the results of thefunction. Check your work in GHCi.

Page 29: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 16

1. a) 8 + 7 * 9b) (8 + 7) * 9

2. a) perimeter x y = (x * 2) + (y * 2)b) perimeter x y = x * 2 + y * 2

3. a) f x = x / 2 + 9b) f x = x / (2 + 9)

1.6 Declaring values

The order of declarations in a source code file doesn’t matter because GHCiloads the entire file at once, so it knows all the values that have beenspecified, no matter what order they appear in. On the other hand, whenyou enter them one-by-one into the REPL, the order does matter.

For example, we can declare a series of expressions in the REPL like this:

Prelude> let y = 10Prelude> let x = 10 * 5 + yPrelude> let myResult = x * 5

As we’ve seen above when we worked with the triple function, we have touse let to declare something in the REPL.

We can now just type the names of the values and hit enter to see theirvalues:

Prelude> x60Prelude> y10Prelude> myResult300

Page 30: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 17

To declare the same values in a file, such as learn.hs, we write the follow-ing:

-- learn.hs

module Learn where-- First, we declare the name of our module so-- it can be imported by name in a project.-- We won't be doing a project of this size-- for a while yet.

x = 10 * 5 + y

myResult = x * 5

y = 10

Remember module names are capitalized, unlike function names. Also, inthis function name, we’ve used camelBack case: the first letter is still lower-case, but we use an uppercase to delineate a word boundary for readability.

Troubleshooting

It is easy to make mistakes in the process of typing learn.hs in. We’ll lookat a few common mistakes in this section. One thing to keep in mind is thatindentation of Haskell code is significant and can change the meaning of thecode. Incorrect indentation of code can also break your code. Reminder:use spaces not tabs to indent your source code.

In general, whitespace is significant in Haskell. Efficient use of whitespacemakes the syntax more concise. This can take some getting used to ifyou’ve been working in another programming language. Whitespace is oftenthe only mark of a function call, unless parentheses are necessary due toconflicting precedence. Trailing whitespace, that is, extraneous whitespacesat the end of lines of code, is considered bad style.

Page 31: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 18

In source code files, indentation is important and often replaces syntacticmarkers like curly brackets, semicolons, and parentheses. The basic ruleis that code that is part of an expression should be indented under thebeginning of that expression, even when the beginning of the expression isnot at the leftmost margin. Furthermore, parts of the expression that aregrouped should be indented to the same level. For example, in a block ofcode introduced by let or do, you might see something like this:

letx = 3y = 4

-- or

let x = 3y = 4

-- Note that this code won't work directly in a-- source file without embedding in a-- top-level declaration

Notice that the two definitions that are part of the expression line up ineither case. It is incorrect to write:

let x = 3y = 4

-- or

letx = 3y = 4

If you have an expression that has multiple parts, your indentation willfollow a pattern like this:

Page 32: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 19

foo x =let y = x * 2

z = x ^ 2in 2 * y * z

Notice that the definitions of y and z line up, and the definitions of letand in are also aligned. As you work through the book, try to pay carefulattention to the indentation patterns as we have them printed. There aremany cases where improper indentation will actually cause code not towork.

Also, when you write Haskell code, we reiterate here that you want to usespaces and not tabs for indentation. Using spaces will save you a nontrivialamount of grief. If you don’t know how to make your editor only use spacesfor indentation, look it up!

If you make a mistake like breaking up the declaration of x such that therest of the expression began at the beginning of the next line:

module Learn where-- First declare the name of our module so it-- can be imported by name in a project.-- We won't do this for a while yet.

x = 10* 5 + y

myResult = x * 5

y = 10

You might see an error like:

Prelude> :l code/learn.lhs[1 of 1] Compiling Learn

code/learn.lhs:10:1: parse error on input ‘*’Failed, modules loaded: none.

Page 33: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 20

Note that the first line of the error message tells you where the error oc-curred: code/learn.lhs:10:1 indicates that the mistake is in line 10,column 1, of the named file. That can make it easier to find the problemthat needs to be fixed. Please note that the exact line and column numbersin your own error messages might be different from ours, depending on howyou’ve entered the code into the file.

The way to fix this is to either put it all on one line, like this:

x = 10 * 5 + y

or to make certain that when you break up lines of code that the secondline begins at least one space from the beginning of that line (either of thefollowing should work):

x = 10* 5 + y

-- or

x = 10* 5 + y

The second one looks a little better, and generally you should reserve break-ing up of lines for when you have code exceeding 100 columns in width.

Another possible error is not starting a declaration at the beginning (left)column of the line:

-- learn.hs

module Learn where

x = 10 * 5 + y

myResult = x * 5

y = 10

Page 34: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 21

See that space before x? That will cause an error like:

Prelude> :l code/learn.lhs[1 of 1] Compiling Learn

code/learn.lhs:11:1: parse error on input ‘myResult’Failed, modules loaded: none.

This may confuse you, as myResult is not where you need to modify yourcode. The error is only an extraneous space, but all declarations in themodule must start at the same column. The column all declarations withina module must start in is determined by the first declaration in the mod-ule. In this case, the error message gives a location that is different fromwhere you should fix the problem because all the compiler knows is thatthe declaration of x made a single space the appropriate indentation for alldeclarations within that module, and the declaration of myResult began acolumn too early.

It is possible to fix this error by indenting the myResult and y declarationsto the same level as the indented x declaration:

-- learn.hs

module Learn where

x = 10 * 5 + y

myResult = x * 5

y = 10

However, this is considered bad style and is not standard Haskell practice.There is almost never a good reason to indent all your declarations in thisway, but noting this gives us some idea of how the compiler is reading thecode. It is better, when confronted with an error message like this, to makesure that your first declaration is at the leftmost margin and proceed fromthere.

Page 35: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 22

Another possible mistake is that you might’ve missed the second - in the-- used to comment out source lines of code.

So this code:

- learn.hs

module Learn where-- First declare the name of our module so it-- can be imported by name in a project.-- We won't do this for a while yet.

x = 10 * 5 + y

myResult = x * 5

y = 10

will cause this error:

Prelude> :r[1 of 1] Compiling Main

code/learn.lhs:7:1: parse error on input ‘module’Failed, modules loaded: none.

Note again that it says the parse error occurred at the beginning of themodule declaration, but the issue is actually that - learn.hs had only one- when it needed two to form a syntactically correct Haskell comment.

Now we can see how to work with code that is saved in a source file fromGHCi without manually copying and pasting the definitions into our REPL.Assuming we open our REPL in the same directory as we have learn.hssaved, we can do the following:

Prelude> :load learn.hs[1 of 1] Compiling Learn

Page 36: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 23

Ok, modules loaded: Learn.Prelude> x60Prelude> y10Prelude> myResult300

Intermission: Exercises

The following code samples are broken and won’t compile. The first twoare as you might enter into the REPL; the third is from a source file. Findthe mistakes and fix them so that they will.

1. let area x = 3. 14 * (x * x)

2. let double x = y * 2

3. x = 7y = 10

f = x + y

1.7 Arithmetic functions in Haskell

Let’s work with a wider variety of arithmetic functions in Haskell. Wewill use some common operators and functions for arithmetic. The infixoperators, their names, and their use in Haskell are listed here:

Page 37: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 24

Operator Name Purpose/application+ plus addition- minus substraction* asterisk multiplication/ slash fractional divisiondiv divide integral division, round downmod modulo remainder after divisionquot quotient integral division, round towards zerorem remainder remainder after division

At the risk of stating the obvious, “integral” division refers to division ofintegers. Because it’s integral and not fractional, it takes integers as argu-ments and returns integers as results. That’s why the results are rounded.

Here’s an example of each in the REPL:

Prelude> 1 + 12Prelude> 1 - 10Prelude> 1 * 11Prelude> 1 / 11.0Prelude> div 1 11Prelude> mod 1 10Prelude> quot 1 11Prelude> rem 1 10

Always use div for integral division unless you know what you’re doing.However, if the remainder of a division is the value you need, rem is usuallythe function you want. We will look at / in more detail later, as that willrequire some explanation of types and typeclasses.

Page 38: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 25

1.8 Negative numbers

Due to the interaction of parentheses, currying, and infix syntax, negativenumbers get special treatment in Haskell.

If you want a value that is a negative number by itself, this will work justfine:

Prelude> -1000-1000

However, this will not work in some cases:

Prelude> 1000 + -9<interactive>:3:1:

Precedence parsing errorcannot mix ‘+’ [infixl 6] andprefix `-` [infixl 6]

in the same infix expression

Fortunately, we were told about our mistake before any of our code wasexecuted. Note how the error message tells you the problem has to dowith precedence. Addition and subtraction have the same precedence (6),and GHCi doesn’t know how to resolve the precedence and evaluate theexpression. We need to make a small change before we can add a positiveand a negative number together:

Prelude> 1000 + (-9)991

The negation of numbers in Haskell by the use of a unary - is a form ofsyntactic sugar. Syntax is the grammar and structure of the text we use toexpress programs, and syntactic sugar is a means for us to make that texteasier to read and write. Syntactic sugar is so-called because while it canmake the typing or reading of the code nicer, it changes nothing about the

Page 39: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 26

semantics of our programs and doesn’t change how we solve our problems incode. Typically when code with syntactic sugar is processed by our REPLor compiler, a simple transformation from the shorter (”sweeter”) form toa more verbose, truer representation is performed after the code has beenparsed.

In the specific case of -, the syntax sugar means the operator now has twopossible interpretations. The two possible interpretations of the syntactic -are that - is being used as an alias for negate or that it is the subtractionfunction. The following are semantically identical (that is, they have thesame meaning, despite different syntax) because the - is translated intonegate:

Prelude> 2000 + (-1234)766

Prelude> 2000 + (negate 1234)766

Whereas this is a case of - being used for subtraction:

Prelude> 2000 - 1234766

Fortunately, syntactic overloading like this isn’t common in Haskell.

1.9 Parenthesizing infix functions

There are times when you want to refer to an infix function without applyingany arguments, and there are also times when you want to use them asprefix operators instead of infix. In both cases you must wrap the operatorin parentheses. We will see more examples of the former case later in thebook. For now, let’s look at how we use infix operators as prefixes.

If your infix function is >> then you must write (>>) to refer to it as a value.(+) is the addition infix function without any arguments applied yet and

Page 40: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 27

(+1) is the same addition function but with one argument applied, makingit return the next argument it’s applied to plus one:

Prelude> 1 + 23Prelude> (+) 1 23Prelude> (+1) 23

This same notion works for (-), the subtraction function:

Prelude> 2 - 11Prelude> (-) 2 11

In contrast to the addition example above, though, the following won’twork:

Prelude> (-2) 1

Enclosing a value inside the parentheses with the - indicates to GHCi thatit’s the argument of a function. Because the - function represents negation,not subtraction, when it’s applied to a single argument, GHCi does notknow what to do with that, and so it returns an error message. Here, - isa case of syntactic overloading disambiguated by how it is used.

1.10 Laws for quotients and remainders

Programming often makes use of more division and remainder functionsthan standard arithmetic does, and it’s helpful to be familiar with the lawsabout quot/rem and div/mod.1 We’ll take a look at those here.

Page 41: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 28

(quot x y)*y + (rem x y) == x

(div x y)*y + (mod x y) == x

We won’t walk through a proof exercise, but we can demonstrate these lawsa bit:

(quot x y)*y + (rem x y)

Given x is 10 and y is (-4)

(quot 10 (-4))*(-4) + (rem 10 (-4))

quot 10 (-4) == (-2) and rem 10 (-4) == 2

(-2)*(-4) + (2) == 10

10 == x == yeppers.

Now for div/mod:

(div x y)*y + (mod x y)

Given x is 10 and y is (-4)

(div 10 (-4))*(-4) + (mod 10 (-4))

div 10 (-4) == (-3) and mod 10 (-4) == -2

(-3)*(-4) + (-2) == 10

10 == x == yeppers.1From Lennart Augustsson’s blog http://augustss.blogspot.com/

Page 42: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 29

1.11 Evaluation

When we talk about reducing an expression, we’re talking about evaluatingthe terms until it reaches its simplest form. Once a term has reached itssimplest form, we say it is irreducible or finished evaluating. Usually, we callthis a value. Haskell uses a non-strict evaluation (sometimes called “lazyevaluation”) strategy which defers evaluation of terms until they’re forcedby other terms referring to them. We will return to this concept severaltimes throughout the book as it takes time to fully understand.

Values are irreducible, but applications of functions to arguments are re-ducible. Reducing an expression means evaluating the terms until you’releft with an irreducible value. As in the lambda calculus, application isevaluation, another theme that we will return to throughout the book.

Values are expressions, but cannot be reduced further. Values are a terminalpoint of reduction:

1"Icarus"

The following expressions can be reduced (evaluated, if you will) to a value:

1 + 12 * 3 + 1

Each can be evaluated in the REPL, which reduces the expressions andthen prints what it reduced to.

1.12 Chapter Exercises

The goal for all the following exercises is just to get you playing with codeand forming hypotheses about what it should do. Read the code carefully,using what we’ve learned so far. Generate a hypothesis about what you

Page 43: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 30

think the code will do. Play with it in the REPL and find out where youwere right or wrong.

Parenthesization

Here we’ve listed the information that GHCi gives us for various infix op-erators. We have left the type signatures in this time, although it is notdirectly relevant to the following exercises. This will give you a chance tolook at the types if you’re curious and also provide a more accurate pictureof the :info command.

Prelude> :info (^)(^) :: (Num a, Integral b) => a -> b -> ainfixr 8 ^

Prelude> :info (*)class Num a where(*) :: a -> a -> a

infixl 7 *

Prelude> :info (+)class Num a where(+) :: a -> a -> a

infixl 6 +

Prelude> :info (-)class Num a where(-) :: a -> a -> a

infixl 6 -

Prelude> :info ($)($) :: (a -> b) -> a -> binfixr 0 $

We should take a moment to explain and demonstrate the ($) operator asyou will run into it fairly frequently in Haskell code. The good news is it

Page 44: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 31

does almost nothing. The bad news is this fact sometimes trips people up.

First, here’s the definition of ($):

f $ a = f a

Immediately this seems a bit pointless until we remember that it’s definedas an infix operator with the lowest possible precedence. The ($) operatoris a convenience for when you want to express something with fewer pairsof parentheses.

Example of ($) in some expressions:

Prelude> (2^) $ 2 + 216Prelude> (2^) (2 + 2)16Prelude> (2^) 2 + 26

If you like, a way to understand ($) in words is: “evaluate everything tothe right of me first.”

Also note that you can stack up multiple uses of ($) in the same expression.For example, this works:

Prelude> (2^) $ (+2) $ 3*2256

But this does not:

Prelude> (2^) $ 2 + 2 $ (*30)-- A rather long and ugly type error about trying to-- use numbers as if they were functions follows.

We can see for ourselves why this code doesn’t make sense if we examinethe reduction steps.

Page 45: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 32

-- Remember ($)'s definitionf $ a = f a

(2^) $ 2 + 2 $ (*30)-- Given the right-associativity (infixr) of $-- we must begin at the right-most position.2 + 2 $ (*30)-- reduce ($)(2 + 2) (*30)-- then we must evaluate (2 + 2) before we can apply it4 (*30)-- This doesn't make sense, we can't apply 4-- as if it was a function to the argument (*30)!

Now let’s flip that expression around a bit so it works and then walk througha reduction:

(2^) $ (*30) $ 2 + 2-- must evaluate right-side first(2^) $ (*30) 2 + 2-- application of the function (*30) to the-- expression (2 + 2) forces evaluation(2^) $ (*30) 4-- then we reduce (*30) 4(2^) $ 120-- reduce ($) again.(2^) 120-- reduce (2^)1329227995784915872903807060280344576

Given what we know about the precedence of (*), (+), and (ˆ), how canwe parenthesize the following expressions more explicitly without changingtheir results? Put together an answer you think is correct, then test in theGHCi REPL.

Example:

Page 46: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 33

-- We want to make this more explicit2 + 2 * 3 - 3

-- this will produce the same result2 + (2 * 3) - 3

Attempt the above on the following expressions.

1. 2 + 2 * 3 - 1

2. (^) 10 $ 1 + 1

3. 2 ^ 2 * 4 ^ 5 + 1

Equivalent expressions

Which of the following pairs of expressions will return the same result whenevaluated? Try to reason them out in your head by reading the code andthen enter them into the REPL to check your work:

1. 1 + 1

2

2. 10 ^ 2

10 + 9 * 10

3. 400 - 37

(-) 37 400

4. 100 `div` 3

100 / 3

Page 47: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 34

5. 2 * 5 + 18

2 * (5 + 18)

Names and values

We can use let and where to introduce names for expressions.

Example of where:

-- FunctionWithWhere.hsmodule FunctionWithWhere where

printInc n = print plusTwowhere plusTwo = n + 2

And if we use this in the REPL:

Prelude> :l FunctionWithWhere.hs[1 of 1] Compiling FunctionWithWhere ...Ok, modules loaded: FunctionWithWhere.Prelude> printInc 13Prelude>

Now the same function, but using let in the place of where:

-- FunctionWithLet.hsmodule FunctionWithLet where

printInc2 n = let plusTwo = n + 2in print plusTwo

When you see let followed by in you’re looking at a let expression. Here’sthat function in the REPL:

Page 48: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 35

Prelude> :load FunctionWithLet.hs[1 of 1] Compiling FunctionWithLet ...Ok, modules loaded: FunctionWithLet.Prelude> printInc2 35

If you loaded the FunctionWithLet version of printInc2 in the sameREPL session as FunctionWithWhere then it will have unloaded the oldversion before loading the new one. That is one limitation of the :loadcommand in GHCi. As we build larger projects that require having multiplemodules in scope, we will use a project manager called Cabal and a cabalrepl rather than GHCi itself.

Here’s a demonstration of what is meant by GHCi unloading everythingthat had been defined in the REPL when you :load a file:

Prelude> :load FunctionWithWhere.hs[1 of 1] Compiling FunctionWithWhere ...Ok, modules loaded: FunctionWithWhere.Prelude> printprint printIncPrelude> printInc 13Prelude> :load FunctionWithLet.hs[1 of 1] Compiling FunctionWithLet ...Ok, modules loaded: FunctionWithLet.Prelude> printInc2 1012Prelude> printInc 10

<interactive>:6:1:Not in scope: ‘printInc’Perhaps you meant ‘printInc2’ (line 4)

printInc isn’t in scope anymore because GHCi unloaded everything you’ddefined or loaded after you used :load to load the FunctionWithLet.hssource file. Scope is the area of source code where a binding of a variableapplies.

Page 49: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 36

Now for more exercises. First, determine in your head what the followingexpressions will return, then validate in the REPL:

1. let x = 5 in x

2. let x = 5 in x * x

3. let x = 5; y = 6 in x * y

4. let x = 3; y = 1000 in x + 3

Let and where

Above, you entered some let expressions into your REPL to evaluate them.Now, we’re going to open a file and rewrite some let expressions as func-tions using where clauses. You will have to give the function a name,although the name can be just a letter if you like. For example,

let x = 5; y = 6 in x * y

could be rewritten as

mult1 = x * ywhere x = 5

y = 6

Making the equals signs line up is a stylistic choice. As long as the expres-sions are nested in that way, the equals signs do not have to line up. Butnotice we use a function name that we will use to call this function in theREPL:

*Main> :l practice.hs[1 of 1] Compiling MainOk, modules loaded: Main.*Main> mult130

Page 50: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 37

Note: the filename you choose is unimportant except for the .hs extension.

Rewrite the following let expressions as functions with where clauses:

1. let x = 3; y = 1000 in x * 3 + y

2. let y = 10; x = 10 * 5 + y in x * 5

3. let x = 7; y = negate x; z = y * 10 in z / x + y

More fun with functions

Here is a bit of code as it might be entered into a source file. Rememberthat when you write code in a source file, the order is unimportant, butwhen writing code directly into the REPL the order does matter. Giventhat, look at this code and rewrite it such that it could be evaluated inthe REPL (remember: you’ll need let when entering it directly into theREPL). Be sure to enter your code into the REPL to make sure it evaluatescorrectly.

z = 7

x = y ^ 2

waxOn = x * 5

y = z + 8

1. Now you have a value called waxOn in your REPL. What do you thinkwill happen if you enter:

10 + waxOn-- or(+10) waxOn-- or(-) 15 waxOn-- or(-) waxOn 15

Page 51: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 38

2. Earlier we looked at a function called triple. While your REPL hasthe waxOn function in session, re-enter the triple function at theprompt:

let triple x = x * 3

3. Now, what will happen if we enter this at our GHCi prompt. Tryto reason out what you think will happen first, considering what rolewaxOn is playing in this function call. Then enter it, see what doeshappen, and check your understanding:

triple waxOn

4. Rewrite waxOn as a function with a where clause in your source file.Load it into your REPL and make sure it still works as expected!

5. Now to the same source file where you have waxOn, add the triplefunction. Remember: You don’t need let and the function nameshould be at the left margin (that is, not nested as one of the waxOnexpressions). Make sure it works by loading it into your REPL andthen entering triple waxOn again at the REPL prompt. You shouldhave the same answer as you did above.

6. Now, without changing what you’ve done so far in that file, add anew function called waxOff that looks like this:

waxOff x = triple x

7. Load the source file into your REPL and enter waxOff waxOn at theprompt.You now have a function, waxOff that can be applied to a variety ofarguments – not just waxOn but any (numeric) value you want to putin for x. Play with that a bit. What is the result of waxOff 10 or waxOff(-50)? Try modifying your waxOff function to do something new –perhaps you want to first triple the x value and then square it or divideit by 10. Just spend some time getting comfortable with modifyingthe source file code, reloading it, and checking your modification inthe REPL.

Page 52: Haskell Book Sample

CHAPTER 1. HELLO, HASKELL!: BASIC EXPRESSIONS ANDFUNCTIONS 39

Definitions

1. An argument (also, parameter) is an input to a function. Where wehave the function f x = x + 2 which takes an argument and returnsthat value added to 2, x is the argument or parameter to our function.

2. An expression is a combination of symbols that conforms to syntacticrules and can be evaluated to some result. In Haskell, an expression isa well-structured combination of constants, variables, and functions.While irreducible constants are technically expressions, we usuallyrefer to those as “values”, so we usually mean “reducible expression”when we use this term.

3. A redex is a reducible expression.

4. A value is an expression that cannot be reduced or evaluated anyfurther. 2 * 2 is an expression, but not a value, whereas what itevaluates to, 4, is a value.

5. A function is a mathematical object whose capabilities are limited tobeing applied to an argument and returning a result. Functions canbe described as a list of ordered pairs of their inputs and the resultingoutputs, like a mapping. Given the function f x = x + 2 applied tothe argument 2, we would have the ordered pair (2, 4) of its inputand output.

6. Infix notation is the style used in arithmetic and logic. Infix meansthe the operator is placed between the operands or arguments. Anexample would be the plus sign in an expression like 2 + 2.

7. Operators are functions that are infix by default. In Haskell, operatorsmust use symbols and not alphanumeric characters.

8. Syntactic sugar is syntax within a programming language designed tomake expressions easier to write or read.

Page 53: Haskell Book Sample

Chapter 2

StringsSimple operations with text

Like punning, programming is aplay on words

Alan Perlis

40

Page 54: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 41

2.1 Printing strings

So far we’ve been looking at doing arithmetic using simple expressions. Inthis section, we will look at another type of basic expression that processesa different type of data called Strings.

Most programming languages refer to the data structures used to containtext as “strings,” usually represented as sequences, or lists, of characters.In this section, we will

• take an introductory look at types to understand the data structurecalled String;

• talk about the special syntax, or syntactic sugar, used for Strings;

• print strings in the REPL environment;

• work with some simple functions that operate on this datatype.

2.2 A first look at types

First, since we will be working with Strings, we want to start by under-standing what these data structures are in Haskell and a bit of specialsyntax we use for them. We haven’t talked much about types yet, althoughyou saw some examples of them in the last chapter. Types are importantin Haskell, and the next two chapters are entirely devoted to them.

Types are a way of categorizing values. There are several types for numbers,for example, depending on whether they are integers, fractional numbers,etc. There is a type for boolean values, specifically the values True andFalse. The types we are primarily concerned with in this chapter are Char‘character’ and String. Strings are lists of characters.

It is easy to find out the type of a value, expression, or function in GHCi.We do this with the :type command.

Open up your REPL, enter :type 'a' at the prompt, and you should seesomething like this:

Page 55: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 42

Prelude> :type 'a''a' :: Char

We need to highlight a few things here. First, we’ve enclosed our characterin single quotes. This lets GHCi know that the character is not a variable.If you enter :type a instead, it will think it’s a variable and give you anerror message that the a is not in scope. That is, the variable a hasn’t beendefined (is not in scope), so it has no way to know what the type of it is.

Second, the :: symbol is read as “has the type.” You’ll see this often inHaskell. Whenever you see that double colon, you know you’re looking ata type signature. A type signature is a line of code that defines the typesfor a value, expression, or function.

And, finally, there is Char, the type. Char is the type that includes alpha-betic characters, unicode characters, symbols, etc. So, asking GHCi :type'a', that is, “what is the type of ’a’?”, gives us the information, 'a' ::Char, that is, “’a’ has the type of Char.”

Now, let’s try a string of text. This time we have to use double quotationmarks, not single, to tell GHCi we have a string, not a single character:

Prelude> :type "Hello!""Hello!" :: [Char]

We have something new in the type information. The square bracketsaround Char here are the syntactic sugar for a list. When we talk aboutlists in more detail later, we’ll see why this is considered syntactic sugar;for now, we just need to understand that GHCi says “Hello!” has the typelist of Char.

2.3 Printing simple strings

Now, let’s look at some simple commands for printing strings of text in theREPL:

Page 56: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 43

Prelude> print "hello world!""hello world!"

Here we’ve used the command print to tell GHCi to print the string to thedisplay, so it does, with the quotation marks still around it.

Other commands we can use to tell GHCi to print strings of text into thedisplay have slightly different results:

Prelude> putStrLn "hello world!"hello world!Prelude>

Prelude> putStr "hello world!"hello world!Prelude>

You can probably see that putStr and putStrLn are similar to each other,with one key difference. We also notice that both of these commands printthe string to the display without the quotation marks. This is because,while they are superficially similar to print, they actually have a differenttype than print does. Functions that are similar on the surface can behavedifferently depending on the type or category they belong to.

Next, let’s take a look at how to do these things from source files. Typethe following into a file named print1.hs:

-- print1.hsmodule Print1 where

main :: IO ()main = putStrLn "hello world!"

Here’s what you should see when you run this code by loading it in GHCiand running main:

Prelude> :l print1.hs

Page 57: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 44

[1 of 1] Compiling Print1Ok, modules loaded: Print1.Prelude> mainhello world!Prelude>

The main function is the default function when you build an executable orrun it in a REPL. As you can see, main has the type IO (). IO, that’sthe letters I as in eye and O as in oh, stands for input/output but has aspecialized meaning in Haskell. It is a special type used by Haskell whenthe result of running the program involves side-effects as opposed to beinga pure function or expression. Printing to the screen is a side-effect, soprinting the output of a module must be wrapped in this IO type. Whenyou enter functions directly into the REPL, GHCi implicitly understandsand implements IO without you having to specify that. Since the mainfunction is the default executable function, this bit of code will be in a lotof source files that we build from here on out. We will explain its meaningin more detail in a later chapter.

Let’s start another file:

-- print2.hsmodule Print2 where

main :: IO ()main = doputStrLn "Count to four for me:"putStr "one, two"putStr ", three, and"putStrLn " four!"

And here’s what you should see when you run this one:

Prelude> :l print2.hs[1 of 1] Compiling Print2Ok, modules loaded: Print2.Prelude> main

Page 58: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 45

Count to four for me:one, two, three, and four!Prelude>

For a bit of fun, change the invocations of putStr to putStrLn and viceversa. Rerun the program and see what happens.

You’ll note the putStrLn function prints to the current line, then starts anew line, where putStr prints to the current line but doesn’t start a newone. The Ln in putStrLn indicates that it starts a new line.

String concatenation

Concatenation, or to concatenate something, means to “link together.” Usu-ally when we talk about concatenation in programming we’re talking aboutlinear sequences such as lists or strings of text. If we concatenate twostrings "Curry" and " Rocks!" we will get the string "Curry Rocks!".Note the space at the beginning of " Rocks!". Without that, we’d get"CurryRocks!".

Let’s start a new text file and type the following:

Page 59: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 46

-- print4.hsmodule Print4 where

myGreeting :: String-- The above line reads as: "myGreeting has the type String"myGreeting = "hello" ++ " world!"-- Could also be: "hello" ++ " " ++ "world!"-- to obtain the same result.

hello :: Stringhello = "hello"

world :: Stringworld = "world!"

main :: IO ()main = doputStrLn myGreetingputStrLn secondGreetingwhere secondGreeting = concat [hello, " ", world]

If you execute this, you should see something like:

Prelude> :load print4.hs[1 of 1] Compiling Print4Ok, modules loaded: Print4.*Print4> mainhello world!hello world!*Print4>

Remember you can use :m to return to Prelude if desired.

This little exercise demonstrates a few things:

1. We define values at the top level of a module: (myGreeting, hello,world, and main). That is, they are declared globally so that their

Page 60: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 47

values are available to all functions in the module.

2. We specify explicit types for top-level definitions.

3. We concatenate strings with (++) and concat.

Global versus local definitions

What does it mean for something to be at the top level of a module? Itmeans it is defined globally, not locally. To be locally defined would meanthe declaration is nested within some other expression and is not visible tocode importing the module. We practiced this in the previous chapter withlet and where. Here’s an example for review:

module GlobalLocal where

topLevelFunction :: Integer -> IntegertopLevelFunction x = x + woot + topLevelValuewhere woot :: Integer

woot = 10

topLevelValue :: IntegertopLevelValue = 5

In the above, you could import and use topLevelFunction ortopLevelValue from another module. However, woot is effectively invisibleoutside of topLevelFunction. The where and let clauses in Haskell intro-duce local bindings or declarations. To bind or declare something means togive an expression a name. You could pass around and use an anonymousversion of topLevelFunction manually, but giving it a name and reusingit by that name is more pleasant and less repetitious. Also note we explic-itly declared the type of woot in the where clause. This wasn’t necessary(Haskell’s type inference would’ve figured it out fine), but it was done hereto show you how in case you need to. Be sure to load and run this code inyour REPL:

Prelude> :l Global.hs

Page 61: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 48

[1 of 1] Compiling GlobalLocalOk, modules loaded: GlobalLocal.*GlobalLocal> topLevelFunction 520

Experiment with different arguments and make sure you understand theresults you’re getting by walking through the arithmetic in your head (oron paper).

Intermission: Exercises

1. These lines of code are from a REPL session. Is y in scope for z?

Prelude> let x = 5Prelude> let y = 7Prelude> let z = x * y

2. These lines of code are from a REPL session. Is h in scope for functiong?

Prelude> let f = 3Prelude> let g = 6 * f + h

3. This code sample is from a source file. Is everything we need toexecute area in scope?

area d = pi * (r * r)r = d / 2

4. This code is also from a source file. Now are r and d in scope forarea?

area d = pi * (r * r)where r = d / 2

Page 62: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 49

2.4 Type signatures of concatenation func-tions

Let’s look at the types of (++) and concat. The ++ function is an infixoperator. When we need to refer to an infix operator in a position that isnot infix – such as when we are using it in a prefix position or having itstand alone in order to query its type – we must put parentheses around it.On the other hand, concat is a normal (not infix) function, so parenthesesaren’t necessary:

++ has the type [a] -> [a] -> [a]concat has the type [[a]] -> [a]

-- Here's how we query that in ghci:Prelude> :t (++)(++) :: [a] -> [a] -> [a]Prelude> :t concatconcat :: [[a]] -> [a]

But what do these types mean? Here’s how we can break it down:

(++) :: [a] -> [a] -> [a]-- [1] [2] [3]

Everything after the :: is about our types, not our values. The ‘a’ insidethe list type constructor [] is a type variable.

1. Take an argument of type [a], which is a list of elements whose typewe don’t yet know.

2. Take another argument of type [a], a list of elements whose type wedon’t know. Because the variables are the same, they must be thesame type throughout (a == a).

3. Return a result of type [a]

Page 63: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 50

As we’ll see, because String is a type of list, the operators we use withstrings can also be used on lists of other types, such as lists of numbers.The type [a] means that we have a list with elements of a type a we donot yet know. If we use the operators to concatenate lists of numbers, thenthe a in [a] will be some type of number (for example, integers). If we areconcatenating lists of characters, then a represents a Char because Stringis [Char]. The type variable a in [a] is polymorphic. Polymorphism isan important feature of Haskell. For concatenation, every list must be thesame type of list; we cannot concatenate a list of numbers with a list ofcharacters, for example. However, since the a is a variable at the type level,the literal values in each list we concatenate need not be the same, only thesame type. In other words, a must equal a (a == a).

Prelude> "hello" ++ " Chris""hello Chris"

-- but

Prelude> "hello" ++ [1, 2, 3]

<interactive>:14:13:No instance for (Num Char) arising from the literal ‘1’In the expression: 1In the second argument of ‘(++)’, namely ‘[1, 2, 3]’In the expression: "hello" ++ [1, 2, 3]

In the first example, we have two strings, so the type of a matches – they’reboth Char in [Char], even though the literal values are different. Sincethe type matches, no type error occurs and we see the concatenated result.In the second example, we have two lists (a String and a list of numbers)whose types do not match, so we get the error message. GHCi asks for aninstance of the numeric typeclass Num for the type Char. Unsurprisingly,Char isn’t an instance of Num.

Page 64: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 51

Intermission: Exercises

Read the syntax of the following functions and decide whether it will com-pile. Test them in your REPL and try to fix the syntax errors where theyoccur.

1. ++ [1, 2, 3] [4, 5, 6]

2. '<3' ++ ' Haskell'

3. concat ["<3", " Haskell"]

2.5 An example of concatenation and scop-ing

We will use parentheses to call ++ as a typical prefix (not infix) function:

-- print4flipped.hsmodule Print4Flipped where

myGreeting :: StringmyGreeting = (++) "hello" " world!"

hello :: Stringhello = "hello"

world :: Stringworld = "world!"

main :: IO ()main = doputStrLn myGreetingputStrLn secondGreetingwhere secondGreeting = (++) hello ((++) " " world)-- could've been: secondGreeting = hello ++ " " ++ world

Page 65: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 52

In myGreeting, we moved ++ to the front like a typical prefix functioninvocation. Using it as a prefix function in the secondGreeting, though,forces us to shift some things around. Parenthesizing it that way emphasizesthe right associativity of the ++ function.

The where clause creates local bindings for expressions that are not visibleat the top level. In other words, the where clause in the main functionintroduces a definition visible only within the expression or function it’sattached to, rather than making it globally visible to the entire module.Something visible at the top level is in scope for all parts of the module andmay be exported by the module or imported by a different module. Localdefinitions, on the other hand, are only visible to that one function. Youcannot import into a different module and reuse secondGreeting.

To illustrate:

-- print5broken.hsmodule Print5Broken where

printSecond :: IO ()printSecond = doputStrLn greeting

main :: IO ()main = doputStrLn greetingprintSecondwhere greeting = "Yarrrrr"

You should get an error like this:

Prelude> :l print5broken.hs[1 of 1] Compiling Print5Broken

print5broken.hs:6:12: Not in scope: ‘greeting’Failed, modules loaded: none.Prelude>

Page 66: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 53

Let’s take a closer look at this error:

print5broken.hs:6:12: Not in scope: ‘greeting’# [1][2] [3] [4]

1. The line the error occurred on: in this case, line 6.

2. The column the error occurred on: column 12. Text on computers isoften described in terms of lines and columns. These line and columnnumbers are about lines and columns in your text file containing thesource code.

3. The actual problem. We refer to something not in scope, that is, notvisible to the printSecond function.

4. The thing we referred to that isn’t visible or in scope.

Now make the Print5Broken code compile. It should print “Yarrrrr” twiceon two different lines and then exit.

2.6 More list functions

You can use most functions for manipulating lists on Strings as well inHaskell, because a String is just a list of characters, [Char], under thehood.

Here are some examples:

Prelude> :t 'c''c' :: CharPrelude> :t "c""c" :: [Char] -- List of Char is String, same thing.

-- the : operator, called "cons," builds a listPrelude> 'c' : "hris""chris"

Page 67: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 54

Prelude> 'P' : """P"

-- head returns the head or first element of a listPrelude> head "Papuchon"'P'

-- tail returns the list with the head chopped offPrelude> tail "Papuchon""apuchon"

-- take returns the specified number of elements-- from the list, starting from the left:Prelude> take 1 "Papuchon""P"Prelude> take 0 "Papuchon"""Prelude> take 6 "Papuchon""Papuch"

-- drop returns the remainder of the list after the-- specified number of elements has been dropped:Prelude> drop 4 "Papuchon""chon"Prelude> drop 9001 "Papuchon"""Prelude> drop 1 "Papuchon""apuchon"

-- we've already seen the ++ operatorPrelude> "Papu" ++ "chon""Papuchon"Prelude> "Papu" ++ """Papu"

-- !! returns the element that is in the specified-- position in the list. Note that this is an-- indexing function, and indices in Haskell start

Page 68: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 55

-- from 0. That means the first element of your-- list is 0, not 1, when using this function.Prelude> "Papuchon" !! 0'P'Prelude> "Papuchon" !! 4'c'

2.7 Chapter Exercises

Reading syntax

1. For the following lines of code, read the syntax carefully and decideif they are written correctly. Test them in your REPL after you’vedecided to check your work. Correct as many as you can.

a) concat [[1, 2, 3], [4, 5, 6]]b) ++ [1, 2, 3] [4, 5, 6]c) (++) "hello" " world"d) ["hello" ++ " world]e) 4 !! "hello"f) (!!) "hello" 4g) take "4 lovely"h) take 3 "awesome"

2. Next we have two sets: the first set is lines of code and the other is aset of results. Read the code and figure out which results came fromwhich lines of code. Be sure to test them in the REPL.

a) concat [[1 * 6], [2 * 6], [3 * 6]]b) "rain" ++ drop 2 "elbow"c) 10 * head [1, 2, 3]d) (take 3 "Julie") ++ (tail "yes")

Page 69: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 56

e) concat [tail [1, 2, 3],tail [4, 5, 6],tail [7, 8, 9]]

Can you match each of previous the expressions to one of these resultspresented in a scrambled order?

a) "Jules"b) [2,3,5,6,8,9]c) "rainbow"d) [6,12,18]e) 10

Building functions

1. Given the list-manipulation functions mentioned in this chapter, writefunctions that take the following inputs and return the expected out-puts. Do them directly in your REPL and use the take and dropfunctions you’ve already seen.

Example

-- If you apply your function to this value:"Hello World"-- Your function should return:"ello World"

The following would be a fine solution:

Prelude> drop 1 "Hello World""ello World"

Now write expressions to perform the following transformations, justwith the functions you’ve seen in this chapter. You do not need to doanything clever here.

Page 70: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 57

a) -- Given"Curry is awesome"-- Return"Curry is awesome!"

b) -- Given:"Curry is awesome!"-- Return:"y"

c) -- Given:"Curry is awesome!"-- Return:"awesome!"

2. Now take each of the above and rewrite it in a source file as a gen-eral function that could take different string inputs as arguments butretain the same behavior. Use a variable as the argument to your(named) functions. If you’re unsure how to do this, refresh your mem-ory by looking at the waxOff exercise from the previous chapter andthe GlobalLocal module from this chapter.

3. Write a function of type String -> Char which returns the thirdcharacter in a String. Remember to give the function a name andapply it to a variable, not a specific String, so that it could be reusedfor different String inputs, as demonstrated (feel free to name thefunction something else. Be sure to fill in the type signature and fillin the function definition after the equals sign):

thirdLetter ::thirdLetter x =

-- If you apply your function to this value:"Curry is awesome"-- Your function should return`r'

4. Now change that function so the string input is always the same andthe variable represents the number of the letter you want to return(you can use “Curry is awesome!” as your string input or a differentstring if you prefer).

Page 71: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 58

letterIndex :: Int -> CharletterIndex x =

5. Using the take and drop functions we looked at above, see if youcan write a function called rvrs (an abbreviation of ‘reverse’ usedbecause there is a function called ‘reverse’ already in Prelude, so ifyou call your function the same name, you’ll get an error message).rvrs should take the string “Curry is awesome” and return the result“awesome is Curry.” This may not be the most lovely Haskell code youwill ever write, but it is quite possible using only what we’ve learnedso far. First write it as a single function in a source file. This doesn’tneed, and shouldn’t, work for reversing the words of any sentence.You’re expected only to slice and dice this particular string with takeand drop.

6. Let’s see if we can expand that function into a module. Why would wewant to? By expanding it into a module, we can add more functionslater that can interact with each other. We can also then export it toother modules if we want to and use this code in those other modules.There are different ways you could lay it out, but for the sake ofconvenience, we’ll show you a sample layout so that you can fill in theblanks:

module Reverse where

rvrs :: String -> Stringrvrs x =

main :: IO ()main = print ()

Into the parentheses after print you’ll need to fill in your functionname rvrs plus the argument you’re applying rvrs to, in this case“Curry is awesome.” That rvrs function plus its argument are now theargument to print. It’s important to put them inside the parenthesesso that that function gets applied and evaluted first, and then thatresult is printed.

Page 72: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 59

Of course, we have also mentioned that you can use the $ symbol toavoid using parentheses, too. Try modifying your main function touse that instead of the parentheses.

Page 73: Haskell Book Sample

CHAPTER 2. STRINGS: SIMPLE OPERATIONS WITH TEXT 60

2.8 Definitions

1. A String is a sequence of characters. In Haskell, String is representedby a linked-list of Char values, aka [Char].

2. A type or datatype is a classification of values or data. Types inHaskell determine what values are members of it or inhabit it. Unlikein other languages, datatypes in Haskell by default do not delimit theoperations that can be performed on that data.

3. Concatenation is the joining together of sequences of values. Often inHaskell this is meant with respect to the [] or “List” datatype, whichalso applies to String which is [Char]. The concatenation functionin Haskell is (++) which has type [a] -> [a] -> [a]. For example:

Prelude> "tacos" ++ " " ++ "rock""tacos rock"

4. Scope is where a variable referred to by name is valid. Another wordused with the same meaning are visibility, because if a variable isn’tvisible it’s not in scope.

5. Local bindings are bindings local to particular expression. The primarydelineation here from global bindings is that local bindings cannot beimported by other programs or modules.

6. Global or top level bindings in Haskell mean bindings visible to allcode within a module and, if made available, can be imported by othermodules or programs. Global bindings in the sense that a variable isunconditionally visible throughout an entire program doesn’t exist inHaskell.

7. Data structures are a way of organizing data so that the data can beaccessed conveniently or efficiently.