© Alejandro Serrano Mena 2019
Alejandro Serrano MenaPractical Haskellhttps://doi.org/10.1007/978-1-4842-4480-7_12

12. Web Applications

Alejandro Serrano Mena1 
(1)
Utrecht, The Netherlands
 

Throughout the book, you’ve been preparing for this moment: you know how to save information about clients and products, and you know a couple of algorithms to mine that data for interesting patterns. Now it’s time to create an interface for clients to make purchases. This data will be the input to K-means and Apriori.

The most interoperable and arguably the most widely used way to create an interface for such an application is to write a web application. Your clients will be expecting to just enter a web address in the browser and get the list of products, put them in a virtual cart, and pay at the end. For that reason, this chapter will focus on building web applications using Haskell.

The development of such an application is divided into two parts. In the first part is the back end, which includes a server that will listen for HTTP requests and return and update information about clients, products, and purchases. For this task, you will reuse much of the knowledge from previous chapters and also learn about Spock, a minimalistic web framework for Haskell, and digestive-functors, a library for handling forms using applicative style.

Then, the focus will turn to the front end, that is, the code that ultimately runs in the browsers of your clients, making the correct requests to the back end. Usually this part would be written in JavaScript; but, as you will see, it’s possible to reuse most of your Haskell knowledge if you use a language such as Elm. This will lead to consider how to manage a graphical stateful application in functional style.

Haskell Web Ecosystem

Web applications are becoming the standard way to interact with users. The benefits of this approach are many; you don’t have to create a different executable for each system from which your client may access your application (although you still need to consider interoperability issues between browsers), and you don’t need users to download or execute any binary code. Everything runs smoothly inside the browser.

If you look at Hackage, you can see an increasing number of packages that deal with web applications. These packages range from being traditional, using patterns similar to frameworks in other languages, to experimental ways of composing an application (in this last group, Functional Reactive Programming [FRP] libraries are becoming popular).

Given the number of possibilities, I will give you an overview of the most important libraries you can use for developing a web application, as I did for accessing a database management system. Of course, this chapter cannot possibly cover everything related to web applications in Haskell. It will focus on a handful of libraries: Spock, blaze-html, Hamlet, and digestive-functors are the chosen ones for the back end. For the front end, I’ll introduce a language different from Haskell but based on it: Elm.

Web Frameworks

A web framework is a library or set of libraries intended to be used when developing a web application, covering all possible technological requirements. These requirements usually include a routing system (for linking a URL to a specific piece of code to be executed when this URL is to be served), a templating system (for generating the final HTML output), several kinds of caches, database access, and authentication through different protocols.

In other languages, a web framework is usually a monolithic library that provides everything in one place. However, Haskell developers usually strive for more modularity, and the most common pattern in Hackage is a set of packages that are developed together that share part of their name (e.g., all the packages starting with yesod) but do not require each other in order to be fully functional. This means you can build your web application by pulling different pieces from different projects. Even though using a single framework gives you the most comfortable experience, using several of them does not imply that the resulting code will be messy at all. Indeed, the Time Machine Store presented in this chapter will use routing, templating, and form-handling libraries from different projects all at once.

Happstack is one of the most veteran frameworks in the wild. One of the most prominent uses of Happstack is clckwrks, a content management system. The main package, happstack-server, provides access to almost all the information about HTTP requests and responses, including compression, cookies, or authentication. On top of that, a Happstack user would usually add happstack-foundation as a dependency, which adds a lot of packages that work together well in a Happstack environment. Most often data gets handled via the acid-state package, routing is done with the web-routes package, and form handling takes place via reform. There’s no preferred templating engine although HSP (Haskell Server Pages), Heist, and Hamlet are listed in the documentation. web-routes and reform are based on the same principles that will be discussed in relation to Spock. The ideas behind HSP are similar to other templating languages such as JavaServer Pages (JSP): you write the HTML you want to be generated, decorated with small chunks of code written in Haskell.

Most of the other frameworks do not include their own server. They are built upon a generic interface called Web Application Interface (WAI) . In principle, you could use any server that understands this interface to run your code (as you can use many servlet engines to run Java web applications). But in practice, only the Warp server is used in production. For that reason, most of the other frameworks provide direct access to running a web application using Warp.

Snap is the name of one of those libraries built upon WAI. There are two main features in the design of Snap. The first one is the focus on giving the developers tools for writing modular components that are later composed (called snaplets ), instead of aiming for monolithic applications. The second one is the use of Heist as their choice of templating library, which uses plain HTML in combination with special tags to request information from other layers of the application.

A third choice is Yesod. The main goal of Yesod developers is to make everything as type-safe as possible. The Persistent library that has already been discussed is developed as part of this framework. If you remember, Persistent schemas were described using a small language that was parsed using quasiquotation and Template Haskell. Yesod brings the same ideas to other parts of the web application; routing and templating are specified using those blocks. For templating, the Shakespeare library can output each common language on the Web with the use of several quasiquoters: Hamlet for HTML documents (which will be discussed in this chapter), Cassius and Lucius for style sheets, and Julius for JavaScript code.

Servant is younger than the other frameworks but has a lot of potential. Its goal is also to make Haskell type-safety guarantees work for us. However, the approach is completely different to Yesod’s. Most parts of the application are described using a type, and this type is later used to automatically generate routing, templating, or marshaling to JSON. Due to the more complex nature of the type-level part of a Servant application, to use it proficiently you need to master the techniques described in the next chapter.

The focus in this chapter for routing and general application scaffolding will be Spock,1 a minimalistic web framework with focus on strong types. You can think of it as the Haskell version of Ruby’s Sinatra or Python’s Flask. The main feature it provides is an easy description of routes and an easy way to plug in templating libraries (you will see how to use both blaze-html and Hamlet with it). Being so minimalistic, it doesn’t require many different concepts to be understood in order to start using it. This makes it a perfect point to start looking at Haskell web libraries and later move into more complete ones when you require their extra functionality.

One of the points where Spock is weaker is when handling the information that clients give through forms. The library digestive-functors is one of the most popular libraries to bridge this gap (it’s even the preferred way to do so in Snap) and the choice for this chapter. Remember about the aeson library for handling JSON that was introduced in Chapter 10. digestive-functors also builds upon the applicative style to define forms and validation.

For the previously mentioned packages, there is a lot of documentation, even in the form of tutorials and books, so I suggest you look at them to see the differences for yourself. For an updated list of Haskell web frameworks (including the ones mentioned above and many others) and pointers to their web sites, you can check out the Haskell wiki at wiki.haskell.org/Web/Frameworks .

Compilation to JavaScript

Up to now, the frameworks I have discussed deal with the back end (also known as the server side) of the application. They allow you to generate HTML pages that will be the part that the user will ultimately see. Unfortunately, HTML itself is not able to describe all kinds of behaviors that are expected in a dynamic web page, and developers need to resort to JavaScript. Every modern browser includes a JavaScript interpreter that exposes an enormous amount of functionality to the user and allows for creating sophisticated applications (think of the various web pages that show interactive maps).

The problem is that you may not want to code your entire front end (or client side) in JavaScript. JavaScript is a mixed imperative and prototype-oriented language. Even though it integrates some features from the functional paradigm, its syntax and, more importantly, its concepts are very different from Haskell’s. Using the same language for both the back end and the front end reduces the mental effort you need to make when working on both parts at the same time, and it increases the possibilities of sharing code between the parts.

However, making the browsers support a new language is just an illusion. JavaScript is the language where all the efforts are focused. For that reason, many compilers have included the option to generate JavaScript as output apart from binary code. Then, you can use your preferred programming language to write dynamic web pages and still retain the ubiquity of support that JavaScript has on browsers. Furthermore, these days, the speed and quality of JavaScript implementations are quite impressive. The functional community as a whole has worked in that direction, and there are several Haskell-inspired languages that can be compiled into JavaScript.

Some projects such as GHCJS, WebGHC, and Haste modify the GHC compiler itself to output this alternative representation. These projects have different areas of focus, from allowing seamless execution of any application that can be compiled by GHC into a web environment, replicating the entire runtime, to trying to balance full compatibility with GHC and better interoperability with other JavaScript codes. Unfortunately, it seems that these projects have not caught upon Haskell practitioners, and at the moment of writing using and developing with them is not as simple as using GHC.

An alternative approach is to create a completely new language, but strongly based on the syntax and concepts of Haskell. There are two main projects in this area. PureScript is the closest language to Haskell, and shares with it a powerful type system. The main difference is that code is not executed lazily, it uses the most common execution model inherited from JavaScript instead. The other side of the spectrum is covered by Elm, which removes some Haskell’s features (like type classes) in order to provide a simpler language. But we still have higher-order functions, pattern matching, and many of the defining features of Haskell. Elm is the focus of our section about front end.

Like in the case of web frameworks, many other languages and packages provide support for compiling into JavaScript. You can get an up-to-date list with pointers on documentation at wiki.haskell.org/The_JavaScript_Problem .

RESTful Structure

Before starting with the coding, I will introduce some of the general patterns in the design of the application, which are based on the Representational State Transfer (REST) principles. Many of these concepts are directly encoded by the combinators that Spock provides and are visible in many other web frameworks, both inside and outside the Haskell world.

The core idea is that the web application provides access to a set of resources, which can be queried or modified. The information of a client or a product is an example of resources in the Time Machine Store. Each of these resources is accessed through a unique identifier, which in a web application is a URL. For example, /clients could be the identifier of the list of all clients, and /product/3 may identify the information of the product whose identifier in the database is the number 3.

Given that URLs are central to knowing which data must be queried or affected by a request, the web framework of choice should include good support for specifying URL patterns and point to the right code to execute. Each of these patterns is called a route . For example, in Spock you can describe the route for products as /product/<var>, meaning that when any URL starting with /product/ is found, that code is executed, and the rest of the URL is captured in a variable.

For each resource, an application can have several request methods , each of them being a particular action over the resource. For web applications, which are the focus of this chapter, the most used request methods are GET, which retrieves a particular object or lists the elements in a collection; POST, which is used to create a new element inside a specific resource; PUT, which replaces the information of an entire resource with new information coming with the request; and DELETE, which erases the resource from the system. The REST architecture style imposes several constraints on how each method should behave (e.g., GET should not modify the resource at all). You can read about it in many books about this topic, so I won’t delve into details here.

Finally, for each supported combination of resource identifier and request method, the application should give a response using standard formats. When using REST over HTTP, this means using HTTP status codes (e.g., returning a 404 error when some resource is not found) and encoding the queried information using HTML, XML, or JSON. Furthermore, the consumer can specify a list of supported response types, and the server should try to satisfy that requirement .

Back End with Spock

In this section, you will develop the back end of the Time Machine Store. Because of space constraints, I will focus primarily on querying and updating information about products. Support for clients and purchases is left as a good exercise to put together much of the knowledge you’ve acquired up to this point.

Simple Skeleton

The first thing to do is to set up a project with an executable stanza that will start the server, adding Spock as a dependency.2 You’ve already gone through these steps in the first four chapters. Now repeat them in Exercise 12-1 to create the basis for the store. Remember that exercise solutions are included with this book’s example download from the Apress.​com catalog page.

Exercise 12-1. Setting up the Project

Create a new Cabal package, either using Cabal or Stack, named chapter12. This package should contain a stanza that will generate the time-machine-store executable. Add Spock as a dependency of the executable.

Now you’re ready to start building your web application. The following shows a simple application that just responds to the URL /about:
{-# LANGUAGE OverloadedStrings #-}
import Web.Spock
import Web.Spock.Config
main :: IO ()
main = do
  cfg <- defaultSpockCfg () PCNoDatabase ()
  runSpock 3000 (spock cfg app)
app :: SpockM () () () ()
app = do
  get "about" $
    html $ mconcat [ "<html><body>"
                   , "  <h1>Hello Practical Haskell!</h1>"
                   , "</body></html>"]

The entry point of the application, the main function, starts your Spock application at port 3000. In addition to the port number, the runSpock function needs some configuration parameters, and more importantly, a description of your routes, which in this case we define in app. Right now, there’s only a route, /about, which can be accessed through the GET request method. This route is defined using the name of the method in lowercase letters (get), a pattern which the URL must follow, and finally the handler, which is the code to be executed.

Note

Routes are defined via a value of type Text. The previous code uses the OverloadedStrings extension to write them as string literals.

The result of a request in Spock can be any Text value, which is the data that will be sent over the wire. However, if the data you are returning must be processed as HTML, it’s customary to define the value of some of the response headers. But instead of defining it by hand, Spock provides the html helper function, which sets those headers to the appropriate values and then returns the content.

You can check that your web application works by building the package at the command line and calling it. Then point your browser to http://localhost:3000/about to run the corresponding handler and show the result on your screen.

However, if you surf to another URL on this same server, you will find a prototypical error page. As a small improvement for this simple application, let’s include a custom “not found” page by adding a new route to the do block. In this case, the URL pattern is hookAnyAll , which matches any possible route, and gives it back to the handler as an argument.
import Network.HTTP.Types
app :: SpockM () () () ()
app = do
  get "about" $ ...
  hookAnyAll $ \_route -> do
    setStatus notFound404
    html "<h1>Not found :(</h1>"

One of the most important things to notice is that when the URL is not found, you should set the response status to the corresponding number (in this case, the well-known 404). This is done by calling setStatus , which takes as a parameter one of the codes defined in the Network.HTTP.Types module (this module is not defined inside the package Spock but on http-types, which you should include as an extra dependency).

Caution

Spock checks which route to apply in the same order as they appear in the code. A hookAnyAll route handles every possible URL. Any route after that one will never be executed. For that reason, you must always write the hookAnyAll route as the last one in the description of your application.

Showing Products from the Database

Now let’s move on to the main task in this section: showing information about products. The access to that data will be via the Persistent library (thus, you should include persistent, persistent-template, and persistent-sqlite as dependencies) using the same schema from Chapter 11. In the examples, I will assume that the database schema is described in the module Chapter12.Database.

There are two changes to be done to the main function to make the database available to the handlers. First, you should make sure the schema described via Persistent coincides with the actual database schema. As you know, this is achieved via a migration, which should be the first thing to appear in main.

Each handler wanting to access the database could, in principle, start a new connection using runSqlite and execute the actions there; any Spock action can run any IO action. However, it’s more performant to create a pool of connections. Spock contains native support for this scenario, it only requires changing a little the way we start the application.
import qualified Database.Persist.Sqlite as Db
import Control.Monad.Logger
import Control.Monad.Trans
main :: IO ()
main = do
  Db.runSqlite "example.db" $ Db.runMigration migrateAll
  runNoLoggingT $
    Db.withSqlitePool "example.db" 10 $ pool -> liftIO $ do
      cfg <- defaultSpockCfg () (PCPool pool) ()
      runSpock 3000 (spock cfg app)
app :: SpockM Db.SqlBackend () () ()
app = ...

The creation of the connection pool is done with the usual withSqlitePool function. Note that we do not use the pool yet, instead we initialize the configuration with it by using PCPool, in contrast to our previous use of PCNoDatabase. The type of app also needs to change to reflect that the routes may access a database of type SqlBackend. At any point in the handler you can request a connection via the runQuery method; Spock manages the pool for you.

Coming back to our products, each product will be available through a URL such as /product/n, where n is the unique identifier given by the DBMS to that product. In Spock, having different parts of the route separated by / is described using the (<//>) function. Up to now you have only seen one form those parts can take, namely constant strings like about. Another possibility is to have parameters, which are declared by using var. For each parameter in the route the handler receives a value. You can also restrict the domain of values a parameter can take by assigning it a type. This is usually done with a local type signature, which are available if you enable the ScopedTypeVariables extension.
{-# LANGUAGE ScopedTypeVariables, RecordWildCards #-}
import Data.Text as T
app :: SpockM Db.SqlBackend () () ()
app = do
  get ("product" <//> var) $ (productId :: Integer) -> do
    product <- runQuery $ conn ->
      flip Db.runSqlPersistM conn $
        Db.get $ ProductKey (Db.SqlBackendKey $ fromIntegral productId)
    case product of
      Just (Product { .. }) ->
        html $ mconcat [ "<html><body>"
                       , "<h1>"
                       , T.pack productName
                       , "</h1>"
                       , "<p>"
                       , T.pack productDescription
                       , "</p>"
                       , "</body></html>" ]
      Nothing -> do setStatus notFound404
                    html "<h1>Not found :(</h1>"

The rest of the code is a straightforward access to the database as explained in Chapter 11 for accessing a value via its identifier and composing the returned HTML value. The code uses record wildcards to bind variables from the Product record easily. When a product identifier is not found, the status code of the response is set to 404 using the same approach as for “not found” routes in the previous section.

The code shown previously is quite short and simple, except for one part: the construction of the resulting HTML page, which is done via simple string concatenation. After seeing how many checks a Haskell compiler can do for you in so many realms, you should ask whether there’s some tool for helping writing HTML code, making sure you close all tags, using indentation to discern the document structure, and so on. The answer is positive: I’m going to show you two different libraries that take different paths for this task.

The first option is the blaze-html package . The idea is that each HTML element is represented by a function whose final parameter is a monadic value representing all those tags nested inside. This way, you can write nested do blocks to simulate the document structure in your Haskell code. The other main combinator is (!), which allows you to include HTML attributes for each element.

Since there are several versions of the (X)HTML standard, blaze-html provides different packages defining the accepted tags and attributes for each. In the following code, I’ve rewritten the handler for products to use blaze-html instead of string concatenation. Notice how you need to call toHtml to convert a string into a value that can be consumed by blaze-html and to convert from a blaze-html value into Text that can be returned as part of the handler via a combination of renderHtml and toStrict (because blaze-html uses lazy Text values, but Spock requires strict ones).
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Data.Text.Lazy (toStrict)
import Text.Blaze.Html.Renderer.Text (renderHtml)
app = do
  get ("product" <//> var) $ (productId :: Integer) -> do
    ...
    case product of
      Just (Product { .. }) ->
        html $ toStrict $ renderHtml $
          H.html $ do
            H.head $
              H.title "Time Machine Store"
            H.body $ do
              H.h1 $ H.toHtml productName
              H.p H.! A.id "descr" $ H.toHtml productDescription
      Nothing -> ...
A second choice for embedding HTML values in a type-safe way is to use the templating system called Shakespeare . The distinctive characteristic of this package is its use of quasiquotation to provide different syntax modes closer to the ones used on the web. The one we shall explore here is Hamlet, which returns well-formatted HTML.
{-# LANGUAGE QuasiQuotes, TemplateHaskell #-}
import Text.Hamlet
app = do
  get ("product" <//> var) $ (productId :: Integer) -> do
    ...
    case product of
      Just (Product { .. }) ->
        html $ toStrict $ renderHtml [shamlet|
          <html>
            <head>
               <title>Time Machine Store
            <body>
               <h1>#{productName}
               <p id=descr>#{productDescription}
        |]
      Nothing -> ...

Hamlet templates use opening tags in the same way as HTML. However, indentation is the syntactic element marking the nesting structure, so closing tags are not needed, as you can see in the previous example. At any moment you can “escape” to the Haskell world via #{ }, and the result of the expression inside the brackets will be printed safely at that point.

In this simple web application, the shamlet function is used for parsing the HTML block. This is a simple function that just returns an Html value, which can be rendered using renderHtml (the same as was presented for blaze-html). But Shakespeare provides other quasiquoters that are much more powerful than this.
  • The hamlet function allows you to use type-safe URLs . The idea is to define all possible routes in your web application using an ADT. Each of the constructors will describe a URL pattern along with the set of parameters. Then, instead of building your HTML links by hand, you specify one of these constructors every time you need a link. The web-routes generates most of the boilerplate code needed to use this feature.

  • The ihamlet function adds internationalization support to the mix. You can create a general document structure for your templates and then include different messages for each language that your web application supports.

Finally, for each of these functions, Hamlet provides a corresponding function ending in File (shamletFile, hamletFile, ihamletFile) that reads the template for an external file. In that way, you can separate the view of your data from the logic in the server.

Hamlet includes not only tags and brackets for variables but also control structures for simple cases that are common when building an HTML page. For example, the following code uses $forall to iterate through all the products in the database, writing a row in a table for each of them:
app = do
  ...
  get "products" $ do
    (products :: [Db.Entity Product]) <- runQuery $ conn ->
      flip Db.runSqlPersistM conn $
        Db.selectList [] []
    html $ toStrict $ renderHtml [shamlet|
      <html>
        <body>
          <h1>Products
          <table>
            <tr>
              <th>Name
              <th>Description
            $forall Db.Entity _ p <- products
              <tr>
                <td>#{productName p}
                <td>#{productDescription p}
    |]
As a final touch, you may want to provide not only HTML output but also JSON output for those consumers of your web application that are not end users but rather other developers interacting with your store. The good news if you are using the aeson library is that Spock provides a helpful json function that takes care of converting a value into the corresponding text format, and it sets the correct headers. For example, a new route for getting back information about a product in JSON format is simple to add:
app = do
  ...
  get ("json" <//> "product" <//> var) $ (productId :: Integer) -> do
    product <- runQuery $ conn ->
      flip Db.runSqlPersistM conn $
        Db.get $ ProductKey (Db.SqlBackendKey $ fromIntegral productId)
    case product of
      Just p  -> json p
      Nothing -> setStatus notFound404

Exercise 12-2 asks you to implement the same functionality as you’ve done for products, but now you can do this for clients of the Time Machine Store.

Exercise 12-2. Querying About Clients

Include the new routes /clients, /client/:clientId, and /json/client/:clientId that return information about all clients and a particular client in HTML and JSON formats. Use any of the presented templating systems for generating the HTML output.

Inserting New Products Using Forms

Right now, you have tools only for writing routes that use information from the URL to respond. But when a user is expected to give some data interactively, the interface should present a form, and the web application should take as input the information from that form. How to do this in an elegant and composable way will be the main topic of this section.

Before continuing, it should be noted that Spock provides a set of param* functions to get the value of fields in a form. By default, param looks in both the POST and GET variables, but more concrete versions are provided. The problem with using param the entire time is that the parsing and visualization of forms becomes stripped between different handlers. Furthermore, you need to manually convert from Text to the required format, and deal with erroneous input. You should aim instead to have a centralized description of each form in your web application that can be reused between different handlers (e.g., your product form may be used both for creating a new one and for updating the information of an existing one).

The package you will use in this web application for form handling is called digestive-functors . One interesting feature of this library is its use of the applicative style, which gives forms the same flavor as JSON handling with aeson, which has been already discussed in this book. The digestive-functors package also separates the description of the data and validation for building a value from the way in which that form should be visualized, giving a modular approach to form handling.

Each form you build with this library must build a value of a specific type, which would usually be one of the types you use in your database schema. In addition, the Form type (the one describing each form in the page) needs a type variable describing the format in which errors will be presented. For each argument in the constructor of that type, you must specify which field in your form will handle its value, one of the basic kinds of fields you can have in an HTML form, the initial value for an empty form, and any extra validation required. For example, this is a form for the Country type, which needs a string and a Boolean value and shows the errors as strings. Notice how the field is specified using the string or bool function, and the initial value is specified wrapped in Maybe.
import Text.Digestive
countryForm :: Monad m => Form String m Country
countryForm = Country <$> "name" .: string Nothing
                      <*> "send" .: bool (Just True)

Caution

Both aeson and digestive-functors contain a function called (.:). Be aware of this fact; you will need to qualify at least one of them if you’re using the two modules in the same source file.

For further validation, you must wrap one of those simple specifications inside validate or check. The difference between them is that validate may also parse the value into a new one (maybe of another type), whereas check is only a Boolean predicate that leaves the value as is. Both functions need as extra input the error message that should be provided to the user when the value does not fulfill the requirements. The following code implements a form for Product values; it uses validate to convert from strings to numbers and uses check to constrain the possible items in stock to be larger or equal to zero:
import Text.Digestive.Util
productForm :: Monad m => Form String m Product
productForm
  = Product <$> "name"        .: string Nothing
            <*> "description" .: string Nothing
            <*> "price"       .: validate isANumber (string Nothing)
            <*> "inStock"     .: check "Must be >= 0" (>= 0)
                                   (validate isANumber (string Nothing))
isANumber :: (Num a, Read a) => String -> Result String a
isANumber = maybe (Error "Not a number") Success . readMaybe

Once you have the description of your form, it’s time to define how it will be seen by the user. The code you need to create should take a view of the form (in other words, a definition of the form plus values for each of the fields) and return the HTML output. There are packages for integrating forms with almost any of the templating systems I introduced at the beginning of the chapter. In this section, I will use blaze-html as an example, so you need to add digestive-functors-blaze as a dependency.

The module Text.Digestive.Blaze.Html5 includes functions for all the types of inputs that the HTML standard supports. Each of the functions takes as parameters at least the name of the field it refers to and the view it handles. In addition to input controls, this package can also generate code for input labels and for the errors that concern each field. As an example, here’s a possible way to show the information of a form for a product. Notice the inclusion of inputSubmit for creating a Submit button.
import qualified Text.Blaze.Html5 as H  -- from previous examples
import Text.Digestive.Blaze.Html5
productView :: View H.Html -> H.Html
productView view = do
  form view "/new-product" $ do
    label     "name"    view "Name:"
    inputText "name"    view
    H.br
    inputTextArea Nothing Nothing "description" view
    H.br
    label     "price"   view "Price:"
    inputText "price"   view
    errorList "price"   view
    label     "inStock" view "# in Stock:"
    inputText "inStock" view
    errorList "inStock" view
    H.br
    inputSubmit "Submit"
Now let’s create the form that will be shown when the user of the web application wants to add a new product. Since there’s no information about that new product, you should show the form with the initial data defined in productForm. This is done by creating an empty view using getForm . You can integrate that view’s form in a larger web page by invoking productView, as the following code shows:
app = do
  ...
  get "new-product" $ do
    view <- getForm "product" productForm
    let view' = fmap H.toHtml view
    html $ toStrict $ renderHtml $
      H.html $ do
        H.head $ H.title "Time Machine Store"
        H.body $ productView view'

Note

getForm returns a View String value (since the definition of productForm had type Form String m Product), whereas productView needs a View H.Html value. You can move from the former to the latter by mapping H.toHtml on every field, as the previous code does. The idea behind this conversion is that you should map the errors in the form from the String type to an HTML representation.

When the form is sent by the browser back to the web application, the POST request method is used instead of GET. Thus, in that case you have to read the values sent by the user and then either write the new product on the database if the values are correct or return the same form with error messages. The key point is the change from getForm to runForm , which doesn’t use the initial data but rather a dictionary of fields and values from the request. Except for this change, the code for this request is straightforward. If the input data is valid (runForm returns a Just value), contact the database and redirect to the page for the product; in the other case, return the same form (the productView function will take care of showing the errors). Note that the runForm function lives in a different package, namely Spock-digestive, which you should include as dependency.
import Web.Spock.Digestive
app = do
  ...
  post "new-product" $ do
    (view,product) <- runForm "product" productForm
    case product of
      Just p -> do
        ProductKey (Db.SqlBackendKey newId) <- runQuery $ conn ->
          flip Db.runSqlPersistM conn $ Db.insert p
        redirect $ mconcat ["/product/", T.pack $ show newId]
      Nothing -> do
        let view' = fmap H.toHtml view
        html $ toStrict $ renderHtml $
          H.html $ do
            H.head $ H.title "Time Machine Store"
            H.body $ productView view'

This section serves as a good introduction to building REST web applications in Haskell, in particular using Spock. To fix the concepts in your mind, you should try to complete the back end of the application as Exercise 12-3 suggests. Afterward, if you are interested in web applications in Haskell, you can dive more deeply into Spock (e.g., how to deal with sessions or cookies) or learn about any of the other frameworks that were mentioned earlier in the section “Web Frameworks.”

Exercise 12-3. Registering Clients

Create a new handler for registering clients at route /register. As in the case of products, you need to add handlers for both the GET and POST request methods.

Hint: You may want to read the documentation of the choice combinator in digestive-functors to learn how to build a drop-down list with possible countries in your HTML form.

Front End with Elm

This section is devoted to creating a rich front end for the Time Machine Store web application using Elm. As in the previous section, space constraints limit the discussion to a shallow introduction to the Elm ecosystem and its use to show some products using the back end described above.

Getting Elm in your System

Elm website, located at elm-lang.org , has at the moment of writing a big Install button in its main page. In Windows and Mac it is recommended to use the installer, which also set ups the correct paths in the system; in Linux this set up needs to be done manually.

If you have npm (Node.JS Package Manager) already in your system, you can just run npm install elm --global to get the binaries in the right place. npm is quite popular in the JavaScript world.

In this section, we are going to use one of Elm’s facilities for rapid application development: elm reactor. Usually, in order to use one of these JavaScript-based languages, you need to create an accompanying web page, compile the code to JavaScript, and then call if from the page. Each step is easy, but it creates quite some overhead for a few simple examples. In contrast, using reactor you just need to initialize a folder as a project, and then point your browser directly to an Elm source file. The code is then compiled and executed, without any further configuration.

As I have just mentioned, the first step is setting up a folder as a project. To do so, simply move to that folder in a command line and run elm init. A new elm.json file and a src folder should have been created. The former file describes where is the code located and which are the packages the project depends on:
{
    "type": "application",
    "source-directories": [ "src" ],
    "elm-version": "0.19.0",
    "dependencies": {
        "direct": { ... },
        "indirect": { ... }
    },
    ...
}
Let us begin with a very simple Elm application, which just greets a person. The name of that person will not be subject to change, you just can change it by setting the right variable in the code. Nevertheless, this small application already contains most components of the so-called Elm Architecture.
import Browser
import Html exposing (..)
main = Browser.sandbox { init = init
                       , update = \_ model -> model
                       , view = view }
type alias Model = { currentName : String }
init : Model
init = { currentName = "Alejandro" }
view : Model -> Html ()
view model = div [] [ text "Hello, ", text model.currentName, text "!"]
Before explaining how the application works, let me point out how to use elm reactor to run the application. In the same terminal in which you created the project, run elm reactor.
$ elm reactor
Go to <http://localhost:8000> to see your project dashboard.
Now open a browser and go to the specified website. You will see a list of files. Navigate to the src folder , in which source files should reside, and then click on the file corresponding to your application. If everything has gone well, you should see a big “Hello, Alejandro!” in your screen. Otherwise, the output of the compiler is shown directly in the browser window, which is a very helpful way to diagnose why your application does not work.
  • The simplest version of the Elm Architecture, depicted in Figure 12-1, requires us to provide three components: the model, that is, which is the data our application saves; the update function, which describes how the model is update in response to event such as user input; and the view, which specifies how to render the data in the model as HTML. In turn, the model is defined in two parts: first of all you need to declare a data type which defines all the possible states of the application, and in addition you have to specify the initial state.

../images/316945_2_En_12_Chapter/316945_2_En_12_Fig1_HTML.png
Figure 12-1

Simple Elm Architecture

In this case, the model is simply a string. In the code, though, we use a kind of types which is not available in Haskell’s type system, namely records or row. Our Model type is made up of one single piece of data, which is available under the identifier currentName. If you look closely at the view function below, you can see that we use model.currentName to access the information. These types, inherited from JavaScript, provide a simple way to gather information without the overhead of using constructors everywhere. The init function specifies the initial value for this field; feel free to use your own name there.

The view function describes how to turn a value of the Model type into an HTML document. There is nothing surprising: Elm’s approach to markup follows similar ideas as the blaze-html package we have discussed above.

Using Browser.sandbox we put together the initial state of the model and the view. This function also requires an update function, but for now this function never changes the model. It is not very important here, though, because we have not set up in moment in the application in which an event is raised, and thus the update function shall be called. Let us look at a new version of the application in which you can change the person to be greeted:
import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onClick)
main = Browser.sandbox { init = init, update = update, view = view }
type alias Model = { currentName : String, textboxName : String }
init : Model
init = { currentName = "Alejandro", textboxName = "Alejandro" }
type Msg = TextboxChanged String
         | MakeCurrent
update : Msg -> Model -> Model
update msg model = case msg of
  TextboxChanged nm -> { model | textboxName = nm }
  MakeCurrent       -> { model | currentName = model.textboxName }
view : Model -> Html Msg
view model
  = div []
        [ div [] [ text "Hello, ", text model.currentName, text "!"]
        , input [ placeholder "Write your name here"
                , value model.textboxName
                , onInput TextboxChanged ] []
        , button [ onClick MakeCurrent ] [ text "Greet me!" ]
        ]

The first thing I have done is enlarging the model to incorporate both the current name and the information which is currently saved in a textbox. One possibility would have been to keep the greeting completely in sync with the value of the textbox, but in this example, the user must click a Greet me! button explicitly. This way we look at a bit more complex workflow.

The next place to look at is the definition of view. In addition to the previous line of text, we now have an input element (corresponding to the textbox) and a button element. In both cases, we set up handlers for possible events: in the case of the textbox when the text changes, and in the case of the button whenever it is clicked. In contrast to many graphical interface toolkits, these handlers do not specify functions to run, but rather messages to be sent.

This leads us to the definition of the Msg type, which declares all possible messages that the application can handle. In our case, they correspond closely to the user interface elements, but in general, they might describe any possible event in the application, like a change in the data. The update function is responsible for taking each of the messages and specifying how they affect the underlying model of the application. In this case, the TextboxChanged messages updated the corresponding field in the model, and the MakeCurrent message moves the name from the temporary to definite storage. In turn, this causes the view function to run, leading to visible output for the user.

The right way to think about the Elm Architecture is like a state machine. Each constructor in the Model type defines one state in the application, along with any additional data that it might need. Messages move the application to different states. Apart from this core, we need a way to reflect the changes in the data to the outside; this is the role of the view function. This architecture is a very common way to define user interfaces in the functional world,3 and it has been slowly gaining traction out of this niche. Popular projects like React use very similar ideas.

Retrieving Products

The architecture formed by a model, an update function, and a view works well if your application is self-sufficient. That is, if all the data is contained in the initial state or a product of the user input. But this is not true for many (or even almost all) web applications: the data is not present at the beginning of the application but obtained later by making calls to remote servers (usually your application back end). This behavior requires an extension to the Elm Architecture.

The core idea to model operations which are out of the control of the application, and might be asynchronous, is to use commands . A command represents any operation that we want to request from the outside world – what exactly depends on the library you are using. Once the command finishes working, and has a final value, a message is sent to the application. This message is handled in the same fashion that any user input: by calling the update function which performs any required changes to the model, and then updating the view.

In the following example, we are going to contact the back end we have developed to show the description of a product given its identifier. In particular, we are going to request the information in JSON format, which is the easiest one to parse in Elm. For both tasks we need to add a couple of dependencies to our project. Instead of changing the elm.json file by hand, Elm provides built-in commands to request them:
$ elm install elm/json
I found it in your elm.json file, but in the "indirect" dependencies.
Should I move it into "direct" dependencies for more general use? [Y/n]: y
Dependencies loaded from local cache.
Dependencies ready!
$ elm install elm/http
Here is my plan:
...
Dependencies ready!
Before I dive in the application itself, let us have a look at the way in which Elm handles JSON decoding. As we discussed above, Elm does not feature type classes nor automatic derivation via Generic, so we need to write them by hand. However, these decoders look very similar to the hand-written FromJSON instances you wrote in Chapter 10. The (.:) function, which looked for a named field in an object, is now simply called field; and the decoding of basic types is performed by a set of basic decoders such as string or int.
import Json.Decode exposing (Decoder, map2, field, string)
type alias Product = { name : String, description : String }
productDecoder : Decoder Product
productDecoder = map2 Product (field "name"        string)
                              (field "description" string)
Our model becomes more complex with the introduction of the HTTP call. As in the case of the greeter, we need a piece of data to hold the current input in the textbox, which we can later query. The rest of our Model describes the status of the HTTP call: no request may have been issued yet (because the application JustStarted), the request may be in progress (LoadingProduct), or it might have been either with an error or successfully. In the latter case, we also save the result of the request as a Product.
type alias Model = { productId : String, productStatus : ProductStatus }
type ProductStatus = JustStarted
                   | LoadingProduct
                   | Error
                   | ProductData Product
The set of messages we handle is also quite like our previous application. We have messages coming from changes in the textbox input and clicking the button. To those we add a third one which represents that the HTTP call has finished, so we can look at the result.
import Http
type Msg = TextboxChanged String | Load
         | ReceivedInfo (Result Http.Error Product)
This means that there is one more case to handle in the update function:
import Browser
update : Msg -> Model -> (Model, Cmd Msg)
update msg model = case msg of
  TextboxChanged pid -> ({ model | productId = pid }, Cmd.none)
  Load -> ( { model | productStatus = LoadingProduct }
          , Http.get
             { url = "http://practical.haskell/product/" ++ model.productId
             , expect = Http.expectJson ReceivedInfo productDecoder
             } )
  ReceivedInfo result -> case result of
    Ok p  -> ({ model | productStatus = ProductData p }, Cmd.none)
    Err _ -> ({ model | productStatus = Error },         Cmd.none)
This is not the only change to the function. In this revised version of the Architecture, updates may also issue the commands we have been talking about previously. As a result, the function returns a pair of the model and the list of commands to request. In this case, most updates do not issue a command, which is represented by Cmd.none. In the case of a Load message, we want to make an HTTP call, and if the request is successful, apply the productDecoder and then send the ReceivedInfo message. The init function is also allowed to issue some commands at the beginning of the web application.
init : () -> (Model, Cmd Msg)
init _ = ( { productId = "", productStatus = JustStarted } , Cmd.none )
We still need to put these pieces together. The aforementioned Browser.sandbox is no longer enough when we introduce commands, so we need to change to the more powerful Browser.element.
main = Browser.element { init = init
                       , update = update
                       , subscriptions = \_ -> Sub.none
                       , view = view
                       }

In the code above you can see that Browser.element requires subscriptions in addition to the three components I have been discussed throughout this chapter. By subscribing you can get a stream of events in your application; in contrast to a command, which gives you back only one message. The archetypal example of a subscription is time: you can request to get a tick every n seconds, and the system sends the corresponding message every time the wait is over. In this example, we do not want to subscribe to anything, so we initialize the corresponding field to a function which always returns Sub.none, in the same way that our initial style requests no command using Cmd.none.

The missing piece is the view function to turn the model into HTML elements. Exercise 12-4 asks you to implement this function.

Exercise 12-4. A View for Products

Implement the missing view function in the application above. Remember that you need to have at least one textbox whose changes lead to TextboxChanged messages, and a button that raises a Load message when clicked.

Elm is a very productive language, even though some Haskell features are missing from the language. In fact, its designers argue that a simpler language makes it easier to introduce functional programming in an area of programming – web applications – where only JavaScript has been traditionally used.

Same Origin Policy

If you’re executing all these examples in a local environment, your web page may not work at all. In that case, you’ll have been surely bitten by the Same Origin Policy. This policy, implemented in every browser in the wild, forbids a page in a domain to obtain information from other domains. In this case, a local page may not send information to a web application in localhost:3000, which we have used for our Spock back end.

The easiest solution is to add a header to allow communication from any domain in the Scotty code. This is done by calling setHeader "Access-Control-Allow-Origin" "*" in the handler of the JSON route. In this way, you notify your browser that the server admits calls from everywhere.

Having said that, it’s obvious that you shouldn’t add that header in every web application you create because you may be exposing information to other domains. The best solution is to sever both the back end and front end from the same domain. Alternatively, you can include a restrictive list of allowed clients in the Access-Control-Allow-Origin header.

Summary

This chapter served as an introduction to web application building in Haskell.
  • You learned about the many libraries that the Haskell ecosystem provides for each feature required in a web application: routing, templating, form handling, and so on.

  • The chapter focused on the development of the Time Machine Store following the principles of the REST architectural style.

  • Spock was introduced as a minimalistic web framework, and you saw how to create handlers for different routes and request methods.

  • Two templating systems were discussed: blaze-html, which provide a set of combinators for composing HTML documents, and Hamlet, which uses quasiquotation to embed a markup language inside Haskell code.

  • For handling input from the user, you learned about the digestive-functors library.

  • Finally, Elm was introduced as a way to program rich front-end web pages using a Haskell-inspired language, and you saw a small demo for obtaining remote information.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.227.104