Haskell for madmen: Hello, web server!
Tags: haskell, tutorial
Published: 2019-10-24 10:21:14

---
title: Haskell for madmen: Hello, web server!
series: Haskell for madmen
cover_image: https://thepracticaldev.s3.amazonaws.com/i/u90q8rnyiz6609avk97k.jpg
tags: #Haskell, #tutorial
canonical_url: http://drbearhands.com/haskell-tutorial/3.hello-server/
published: true
---

_Post from my [blog](https://drbearhands.com/haskell-tutorial/3.hello-server/). If you're enjoying the series, please consider [buying me a coffee](https://ko-fi.com/drbearhands)._
Feedback welcome!

# Getting a 3rd party library

Now that we know how to interact with the real world from a functional context, it's time to get a library to help us set up a web server.

First, let us determine what library we need. First we'll go to hackage at [https://hackage.haskell.org/](https://hackage.haskell.org/). We can search for "web server". Generally, you would select a library according to whatever criteria you deem correct, here we will be using Warp, because I say so and it was popular and well maintained at the time of writing this tutorial.

All right, since we've cut our decision process short here, we will be looking at what functions the package exposes later and add warp to our project. We know we require the package "warp", so we add the dependency to the latest version (3.3.0 at the time of writing)

In `package.yaml`, under `dependencies`, let's add the following line:

```yaml
- warp >= 3.3.0 && < 4
```

Now, we rebuild the project with `stack build` and *oh no!* A compile error! (you might not get this error, but it's good to know how to handle it, so read on!)

![Error: While constructing the build plan, the following exceptions were encountered: In the dependencies for haskell-tutorial-0.1.0.0: warp-3.2.28 from stack configuration does not match >=3.3.0 && <4  (latest matching version is 3.3.0) needed since haskell-tutorial is a build target. Some different approaches to resolving this: Set 'allow-newer: true'](https://thepracticaldev.s3.amazonaws.com/i/lq79pa6iev0lvani45a2.png)

What is happening here is that your resolver - the thingy that tries to get all the dependencies of the right version - does not have the package at the version you specified. In this case, we have requested a version newer than the one the resolver has. Stack already suggests 2 options to fix this issue, but there's a caveat: the newer version may be incompatible with other packages! Say you have package A, and A requires package B of version 2.1, and you want to use package B of version 3.0, using the newer B might break A!

So, what we're going to do instead is downgrade warp (note that this is a bad idea if the newer version has security fixes, you should generally check that). Alternatively, we could try to upgrade the resolver, but that can also affect other packages.

Dependency management is a complicated affair anywhere side-effects exist, which includes Haskell through the IO monad and certain unsafe functions. Haskell throws the problems in your face rather than hiding it and potentially failing silently.

So, with the minimum version of warp set to 3.2.28, let's continue.

```bash
stack build
```

# Importing new modules

Now that we have installed a new package, let's find out how we can use it.

Back on the hackage page, navigate to the version you have selected for use in your project, [3.2.28](https://hackage.haskell.org/package/warp-3.2.28) in my case. We see 2 modules: `Network.Wai.Handler.Warp` and `Network.Wai.Handler.Warp.Internal`. We don't care about the internals so open the first one. This will take you to the haddock documentation of the relevant module.

The documentation tells you what functions, types, etc. are exported and is auto-generated by haddock based on code and annotations. Types are a sort of documentation entirely by themselves, which makes generated documentation very useful for Haskell. Humans being what they are, this leads to people putting in less effort. All things considered, I've found docs for Haskell library to be far easier than docs for say, Python and JavaScript. So let's take a look at how to interpret a typical haddock page.

Looking at the top function:

```haskell
run :: Port -> Application -> IO ()
```

The "return type" of the function is an `IO ()`, that's an impure computation, which is what we would expect for a web server. What about the arguments? Following `Port` immediately tells us it's an alias for `Int`. Okay, fair enough, we need to pass in a port number to start a server, makes sense. `Application` turns out to be a bit more complicated:

```haskell
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
```

Okay, so an `Application` must be a function that takes a `Request` - make sense so far, a server should handle requests and give responses - and a function that generates an impure computation of a `ResponseReceived` from a `Response`, to then generate an impure computation of a `ResponseReceived`... right...

Luckily, we are given a code snippet in the documentation to help us:

```haskell
app :: Application
app req respond = bracket_
    (putStrLn "Allocating scarce resource")
    (putStrLn "Cleaning up")
    (respond $ responseLBS status200 [] "Hello World")
```

From the definition of `Application`, we know:
- `app` is a `Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived`
- so `req` must be a `Request`
- and `respond` must be a `Response -> IO ResponseReceived`.  

We can then look at how `respond` is used, but there's this weird `$` operator there.

The `$` operator is a common operator in Haskell that is just used to reduce parentheses. It binds very weakly and applies the right argument to the left one. E.g. `f $ g a` is the same as `f (g a)` (we first apply `a` to `g` and then apply the result to `f`), whereas and `f g a` is the same as `(f g) a` (we first apply `g` to `f` and then apply `a` to the result).

Now we can see that `respond` takes a response, and does something statefull that yields a response received. Because we know we want to send a response (an inherently impure operation, we can make an educated guess about what `respond` does. It sends an http response and returns a "proof" that we have indeed responded. Because we must also provide this proof as the return type of our application, and we have no other way of generating one, the type checker will force us to always provide a response to a request. Neat huh?

All right, all right, there are ways around this restriction by using special functions, but it's a lot harder to accidentally forget to respond.

You may have noticed at this point that when we navigated to the definition of
`Application`, we entered in the documentation for one of Warp's dependencies,
namely Wai. We need to add it to the dependencies too. In my case:

```yaml
- wai >= 3.2.2.1 && < 4
```

With this in mind, we go back to `src/Lib.hs` and modify it:

```haskell
module Lib
    ( someFunc
    ) where

import Network.Wai.Handler.Warp (run)
import Network.Wai (Request, Response, ResponseReceived)

someFunc :: IO ()
someFunc = run 8080 requestHandler

requestHandler :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
requestHandler request respond = error "unimplemented"
```

_(note: you can replace a value of any type with an error in Haskell, which you may or may not like. It's one of the ways of getting around the type check I mentioned above. A full discussion about bottoms and total functional programming is a bit out of scope of this tutorial)_

We can now run the server (`stack run`), but if we attempt to curl to it, it
will throw an error. It will not crash however. By default, warp catches any
error, which is what you'd want in production.

Finally, we're going to create a response.


# Responding

`Response` is an abstract data type, meaning we are given no information about
its internal structure and must rely on the provided functions to create a value
with that type. We look in the documentation for functions with `Response` as
the return type, and find a few:

```haskell
responseFile :: Status -> ResponseHeaders -> FilePath -> Maybe FilePart -> Response
responseBuilder :: Status -> ResponseHeaders -> Builder -> Response
responseLBS :: Status -> ResponseHeaders -> ByteString -> Response
responseStream :: Status -> ResponseHeaders -> StreamingBody -> Response
responseRaw :: (IO ByteString -> (ByteString -> IO ()) -> IO ()) -> Response -> Response
```

That's quite a lot of options. For now, let's keep it simple and use a Lazy ByteString with `responseLBS`.

Again, let's look at the argument types one by one. I skipped the explanation for searching through the documentation as it's no different than what we did earlier.

`Status` is a regular http status code. It's in the `Network.HTTP.Types.Status` module in the `http-types` package. We find the value `status200` in the documentation.

`ResponseHeaders` turns out to be a list of headers. We don't care about headers for now so let's pass the empty list `[]`.

`ByteString` is a small pain in the butt. There are many "strings" in Haskell, but by default we can only write `String`s directly. Luckily we can change that with the OverloadedStrings language extension by adding `{-# LANGUAGE OverloadedStrings #-}` at the top of our module.

We're also going to use a `let ... in` block, which declares values in the `let` part than can be used at the `in` part. E.g.:

```haskell
foo :: Int -> Int
foo x =
  let
    bar :: Int
    bar = x + 1
  in
    x + bar
```

We can now combine our knowledge into the final code for this chapter:


##### `Lib.hs`:
```haskell
{-# LANGUAGE OverloadedStrings #-}

module Lib
    ( someFunc
    ) where

import Network.Wai.Handler.Warp (run)
import Network.Wai (Request, Response, ResponseReceived, responseLBS)
import Network.HTTP.Types.Status (status200)

someFunc :: IO ()
someFunc = run 8080 requestHandler

requestHandler :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
requestHandler request respond =
  let
    response = responseLBS status200 [] "Hello, client!"
  in
    do
      putStrLn "Received an HTTP request!"
      respond response
```


##### `package.yaml` (only the dependencies part, versions may differ):
```yaml
dependencies:
- base >= 4.7 && < 5
- warp >= 3.2.28 && < 4
- wai >= 3.2.2.1 && < 4
- http-types >= 0.12.3 && < 0.13
```


Voila, your first web server in Haskell.

[Final code branch for this chapter](https://gitlab.com/DrBearhands/haskell-tutorial/tree/3-hello-webserver)