Functional Pizza - A delicious Haskell tutorial

November 7, 2015

Welcome! I will help you create a pizzeria - one that works entirely in Haskell - a functional programming language. After completing this tutorial, you will know the basics of the language and have practical experience of using for problem solving. Let’s get started!

Heavily work in progress, bear with me. TODO:

  • improve “flow” / continuation
  • more exercises
  • clickable show/hide for answers
  • add an actual pizza recipe

0. Installation

This tutorial assumes a Linux / Unix environment. If you have a *nix operating system, great! If not, please consider using a virtual machine with a distribution of your choice - setting up programming environments is considerably easier there.

Let’s start by installing the basics.

  • ghc is “The Glorious Glasgow Haskell Compilation System”,
  • ghci is an interactive Haskell REPL, allowing you to try things out in realtime without compilation
  • stack is “a package manager for Haskell”. Comparable to e.g. npm, pip, and so on.

You should install these packages to get started. On most Debian-based systems, this should work:

Some parts of this tutorial refer to Haskell packages that are not installed by default. When you need to install a package, fire up a shell and run:

1. GHCi and basics

Let’s found a pizzeria start-up!

First, we need to make sure it could be a successful business, right? Open up a shell of your choice, and start GHCi by typing ghci and enter. You are greeted with a couple of lines of information - hello, GHCi! o/

Prelude is a bundle of included-by-default libraries and so on. Let’s not let it confuse us - just know that whenever there is Prelude> in the beginning of a line in my code examples, I’m showing you something to try within the GHCi prompt. Later on, when we import more modules, the prompt will show them as well - it’s rather handy, actually!

Math

Now we have a fun little interactive environment to use Haskell in. Here’s some examples of basic math:

Great! Typing in an expression and pressing enter causes GHC to evaluate the expression, and you get the result back.

Exercise: Use GHCi as a calculator. Assuming making a pizza costs you 3€, how many euros profit would you get if you sell 42 pizzas for the price of 5.70€ each?

Answer:

Now a bit about the boolean operators - notice especially how in Haskell, /= is used instead of !=:

Naming

Now that basic math shows that you can make a profit, let’s get going! The starting year is 2015, alright. Your pizzeria is also going to need a name. We could try and combine some words:

Exercise: If we manage to run the pizzeria for seven years, what year will it be?

Answer:

Numbers aren’t words and words aren’t numbers - if we want to combine a number into a string, we need to make a string representation of it. This happens with show:

Hmm. Now that we are actually doing things, maybe we should start putting some notes down and use comments.

Lists

Manipulating lists is the bread and butter of most programming. Knowing that, the great developers have made it easy and pretty in Haskell. Here’s some examples for your pizzeria’s needs:

Generating lists in a puff of flour:

Operating the dough:

Strings are lists of chars:

Word of warning: what is head of an empty list? Or tail of it? Trying to make a pizza crust without dough is not going to compile into a pizza no matter how many toppings you add, you’ll just make a mess of your table :( For the curious and advanced readers, Data.List.Safe might be useful.

In addition to the basic stuff, haskell allows for some fancier things. For example, if you happen to think in mathematical notation:

What happens here is we let evenNumbers be a list of x, where we take each x from a list of numbers from 1 to 10, with the additional condition that x is even.

Functions

Let’s write our first functions! If a customer wants double cheese on their pizza:

One of the fancy things in Haskell is that functions are first-class citizens. Take a look:

TODO: Exercise

2. Hello, world!

Main

Let’s write our first standalone program - in an actual file instead of just poking at the REPL!

Open up a new file in your favourite editor, and name it as hello.hs. Paste this content in it:

Save it, and in a terminal, say the following magic spell: runhaskell hello.hs. Watch the magic happen!

Hi! What's your name?
Reader
You look wonderful today, Reader =)

Let’s take a look at what happened there.

main = do is the most common main function declaration - way shorter and prettier than in more verbose languages! Followed by lines at an increased indentation, it makes up the core part of the program.

putStrLn is a way to print text into the standard output. There is also a function named print, but it behaves a bit differently and might be slightly confusing - it ends up surrounding strings with extra quotes.

name <- getLine - here we bind a value to name, taking the value from a line of standard input. It could be tempting to call this “setting a variable”, but in the Haskell world, they aren’t variable but immutable. Set it once, and that is that. For the sake of understanding the tutorial, yeah, this is a bit like setting variables in other languages.

Helper functions

Now obviously, in many programs, you want other functions than just main. Let’s write a program that asks for a number and returns a number twice the size. We have already done half of it when using GHCi, so this should be easy =)

A new file:

Just like before, we have a main loop that prints some text, binds a value to a name (yeah, almost like storing a variable, people just don’t call it that way in Haskell world), and prints the number doubled. There’s some helper magic going on though:

double is the helper function we wrote earlier. Notice how in actual .hs source code, we don’t use let in the same way as when using GHCi

readInt is a helper function that simply reads a line from stdin, like it says in the function definition: readInt = readLn.

However, what is significant is that we declared a type for it: readInt :: IO Integer means that the function returns a value of the type IO Integer.

If you’re familiar with C, this is a bit like defining a helper function scanf("%d").

However, IO Integer is something that in most cases should be an integer, but because it is an IO operation and hence not “mathematically pure”, it might do something completely different, like cause a side effect. This concept of pure and impure functions and values is rather important in Haskell, and you should get comfortable with it from early on.

In most cases other than reading input, type declaration for functions is completely optional: the compiler infers them. However, it can be very useful to write the types - to catch any misunderstanding between you and the compiler.

Exercise: Write a program that goes through a short discussion with the user. The program could for example ask their name, age, favourite color, and make up a nickname for the user based on that information.

Answer:

There you go, you have your first actual working Haskell program! Congratulations!

If, else

Many times, a function needs to be able to make up its mind and make a decision – for example, if a pizza can be split for kids.

Quite often, if else if pattenrs can become long, difficult to follow, and in general unpleasant to the eye. Don’t worry - in Haskell, if else if pattern has been squeezed down to a single pipe character: |. And easily enough, else is just another else if with the statement otherwise grated on top as the condition. In haskell, these pipe characters are called guards.

Please be careful with the indentation levels.

3. Functional thinking

A family walks into your restaurant! Now that there are real customers in the table, let’s make them happy.

Kids love drinking with straws, and as soon as you arrive at the table you give her a lovely straw with lots of loops. Smiling, she thanks you and starts playing with the straw. Soon enough she asks: “How many loops are there in this straw?”

What is a better way to deal with loops than recursion! To calculate the total number of loops on a squiggly straw, we look at one loop, say “one”, and add the number of the loops on the rest of the straw. So we look at the next loop, add the “one”, and the number of…

Handing the menus - a menu for each member of the family - is a job for map: it takes a function and a list, and applies the function to all elements on the list. Map is basically a for loop, but simpler:

Perhaps the kid is a bit picky: “I don’t want any green circles”. Fair enough, our chef (that’s you!) will filter them out:

Orders are taken, let’s look at the waiter’s note - do some line parsing:

After the family has eaten, it is time to pay up. The amount to pay depends on the price of the individual items ordered, but the amount of tax imposed on restaurant food is the same. We can half-define a multiplication function that is basically “multiply by 1.14” - but doesn’t yet know what to multiply. In Haskell lingo, this is called currying:

That last bit may have sounded a bit odd. Have some background here on Wikipedia - likely to have a stricter dress code than our cozy pizzeria. You have been warned!

4. Data

Let’s define our own data type – a pizza!

Let’s declare that a pizza from our pizzeria is something that has a name, three toppings, and optional garlic and oregano.

It really is as easy as that. You declare names for your fields and the data types of what they contain. deriving Show at the end practically means that our Pizza is printable. (Isn’t it neat where 3d technology is getting us?)

Now let’s define our first pizza - a delicious item on our menu called “Mozza”.

Saved in variable mozza, we now have an instance of our mozzarella pizza. We declared it is a Pizza with all those parameters, in that order. That can get a bit difficult though – trying to remember the order and all. Order all remember the and to trying. What?

Don’t worry, we can also do this – called record syntax:

Hmm. What if someone wants a pizza on our list, but wants to change only one ingredient in it? Do we need to hand-define all the ingredients? Not at all, you can create and use defaults and override only what you need:

If you want to create explicit getters for your newly-created data type, here’s the recipe:


Thank you!

Thank you for taking the time with my tutorial! Hope you enjoyed it and learned something new - and most importantly, had fun =)

There is more content below, it just hasn’t been polished up to the shape of the rest of the tutorial. Feel free to continue reading or come back later to see how the tutorial has progressed.


Extra syntax tips with Haskell

Saving parentheses

Here are some great ways to save on parentheses and keep the code a bit more readable.

$ - the dollar symbol - is a fun way to defer evaluation order: basically, it means “printing is the last important thing we want to do on this line, evaluate everything else first”. That way we can be sure we have the printable string built and ready before printing it. In many other languages, you would need to use parens - in Haskell, you can use either.

In practice, here’s how you use deferred evaluation:

Another way to reduce parentheses is . - known as function composition:

Editing parameter expectations

Sometimes you want to use a function a bit differently than originally intended - maybe give the parameters in a different order to keep the expression “sentence” sound “natural”.

Multi-line definitions

Sometimes function definitions get a bit long, and it might be a good idea to split it up to a couple of lines.

Practical Haskell Cheatsheet

Here are some short snippets of Haskell code that are close to the everyday programming needs - feel free to learn, use and adapt them.

IO

Basic IO operations like printing to stdout, getting from stdin, reading and writing a file.

TODO: fix, is broken :(

Regex

HTTP

After a long day, we need to see what ingredients we have used up and need to restock. A quick look at the pantry and off you go to your trusty old computer, typing your order into your favourite wholesaler’s website:

import Network.HTTP.Conduit -- Remember to `stack install http-conduit`
                            -- Docs https://hackage.haskell.org/package/http-conduit
-- simplest way to get message body, will throw exceptions on http response codes other than 2xx
getBody url = simpleHttp url

-- a hint on how to create custom requests
myLittleRequest <- parseUrl "https://example.com/"
-- will result in:
myLittleRequest =
    Request {
      host                 = "example.com"
      port                 = 443
      secure               = True
      requestHeaders       = []
      path                 = "/"
      queryString          = ""
      method               = "GET"
      proxy                = Nothing
      rawBody              = False
      redirectCount        = 10
      responseTimeout      = Just (-3425)
      requestVersion       = HTTP/1.1
    }

-- entire response as an Response object
response <- withManager $ httpLbs myLittleRequest
-- will result in
response = Response {   responseStatus = Status {statusCode = 200, statusMessage = "OK"},
                        responseVersion = HTTP/1.1,
                        responseHeaders = [("Accept-Ranges","bytes"),("Cache-Control","max-age=604800"),("Content-Type","text/html"),("Date","Thu, 24 Sep 2015 20:35:09 GMT"),("Etag","\"359670651\""),("Expires","Thu, 01 Oct 2015 20:35:09 GMT"),("Last-Modified","Fri, 09 Aug 2013 23:54:35 GMT"),("Server","ECS (ewr/1445)"),("X-Cache","HIT"),("x-ec-custom-error","1"),("Content-Length","1270")],
                        responseBody = "<!doctype html>\n<html>\n<head>\n    <title>Example Domain</title>\n\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <style type=\"text/css\">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n        \n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 50px;\n        background-color: #fff;\n        border-radius: 1em;\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        body {\n            background-color: #fff;\n        }\n        div {\n            width: auto;\n            margin: 0 auto;\n            border-radius: 0;\n            padding: 1em;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</h1>\n    <p>This domain is established to be used for illustrative examples in documents. You may use this\n    domain in examples without prior coordination or asking for permission.</p>\n    <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n",
                        responseCookieJar = CJ {expose = []},
                        responseClose' = ResponseClose}



-- parsing things from the response
responseVersion response -- HTTP/1.1
responseStatus response -- Status {statusCode = 200, statusMessage = "OK"}
responseStatusCode $ responseStatus response -- 200

-- POST

-- TODO: Add HTTP POST examples

JSON

What is the shape of a pizza? A circle of course! For all-a-round JSON-handling, a package most useful is Aeson

Let’s declare that a circle has its center at position x, position y, and has a radius r.

We also want to say that Circle is something we can encode and decode as JSON - and have the generics map haskell object value into a json object value

TODO: make ghci-testable version

Graphics