Haskell framework for the web
Speaking in Tongues
Frameworks for the web are a dime a dozen. Most are based on the PHP and JavaScript programming languages, whereas others use Python, Ruby, Go, or other such languages. When a developer uses WordPress [1], Drupal [2], or Joomla [3], generally they only have to unpack the core on the server and then add other modules. Installing security updates conscientiously and in good time counteracts ubiquitous malware threats.
Quite a few reasons speak to the use of exotic frameworks such as the Haskell-based Yesod [4] [5]. It is not just that Yesod is less widespread, it is also that the security built into the compiler (Haskell is type safe) makes attacks time-consuming or impossible, and SQL and JavaScript injections unlikely. Additionally, the Glasgow Haskell Compiler (GHC) [6], version 1.0 of which has existed since April 1990, is based on the fast C compiler and processes requests to the website far more efficiently than many other programming languages. Because Haskell is a functional programming language, it facilitates other tasks, such as multithreading.
Developers mainly ask themselves one question: How do I get started with the framework? Haskell is considered difficult to learn, because it is strongly orientated on mathematical logic and uses many abstract concepts like monads and category theory. A framework like Yesod helps developers make the transition to Haskell and levels a still steep learning curve, thanks to pre-built components that promise quick results. In this article, I will help you take your first steps with Yesod.
Installation
Stack [7] is available to help you install the framework and set up a software project. The process differs from one operating system to the next, but this article describes its use on Fedora 25, which first needs a couple of packages from the unofficial repository [8] to make Stack operational (Listing 1). To start a new Yesod project, just install the Haskell compiler locally:
cd stack setup
Listing 1
Stack on Fedora 25
dnf install perl make automake gcc gmp-devel libffi zlib zlib-devel xz tar git gnupg dnf-plugins-core dnf copr enable petersen/stack dnf install stack
After the install, the Haskell compiler is located under ~/.stack/programs/x86_64-linux/<GHC version>/bin
. Additional packages that a Yesod project might require can be installed with the simple stack install <package name>
, as needed.
Home Base
Once you have overcome the first hurdle, the real work begins. Yesod supports various popular databases, including MongoDB, SQLite, and, to some extent, Redis. Here, I describe the integration of PostgreSQL [9] and MySQL [10]. To begin, create a new project by entering the command:
stack new <project name> yesod-<database>
Replace <database>
with a valid value, which you can take from a table online [11] (e.g., postgres
or mysql
).
If you want the web application to load data into a MySQL database, the database must be running in the background beforehand (Listing 2). Next, change to the project directory generated by Stack and edit the config/settings.yml
file. Under the database:
heading, adjust the entries for the database user, host, and the name of the database, because otherwise the Haskell compiler will not compile the project.
Listing 2
Setting Up the MySQL DB
mysql -u root -h localhost -p mysql> CREATE DATABASE <mydb>; mysql> CREATE USER '<user>'@'<localhost>' IDENTIFIED BY '<password>'; mysql> GRANT ALL PRIVILEGES ON <mydb.*> TO '<user>'@'<hostname>' IDENTIFIED BY '<password>';
If you want to use a PostgreSQL database for your project, start with the steps in Listing 3. If you do not have high demands on the database, your best database option is SQLite [12], which is intended for a single user only. Yesod creates the database in the project folder, which means that the database is ready for use immediately.
Listing 3
Setting Up the PostgreSQL DB
systemctl start <database> psql -U postgres -c "CREATE EXTENSION adminpack;" postgres=# CREATE USER '<user>' WITH PASSWORD '<password>'; postgres=# CREATE DATABASE <myproject>; postgres=# GRANT ALL PRIVILEGES ON DATABASE <myproject> TO '<user>'; postgres=# \q
First Contact
Stack writes the packages required for compiling the project to the stack.yaml
file. The tool automatically selects the latest snapshot of the Haskell compiler, which can be changed using the entry next to resolver:
; you can choose a snapshot from the Stackage Snapshots page [13]. Alternatively, you can specify the version number of a globally installed Haskell compiler you want to use.
Once you have processed the stack.yaml
file, you can download the required packages with stack build
and install them locally. Next, launch the project in developer mode with the
stack exec --yesod devel
command. Provided you have not changed the domain name, you can call the associated web page in the browser of your choice with http://localhost:3000 (Figure 1).
For a first demonstration, you just need to create a single file called contact.hs
and fill it with the Listing 4 content, which is based on an example from Yesod inventor Michael Snoyman [14] and generates a simple contact form. Being just one file removes the need for project initialization with Stack. The simple web application then calls a simple Stack command:
stack runghc /<Path/to>/contact.hs
Listing 4
contact.hs
01 -- Quasi quotes in curly brackets 02 {-# LANGUAGE ViewPatterns #-} 03 {-# LANGUAGE MultiParamTypeClasses #-} 04 {-# LANGUAGE OverloadedStrings #-} 05 {-# LANGUAGE TypeFamilies #-} 06 {-# LANGUAGE TemplateHaskell, QuasiQuotes #-} 07 -- import libraries 08 import Yesod 09 import Data.Text 10 import Control.Applicative 11 import Yesod.Form 12 - set the web server port Warp to 3000 13 main :: IO () 14 main = warp 3000 FormApp 15 data FormApp = FormApp 16 instance Yesod FormApp 17 instance RenderMessage FormApp FormMessage where 18 renderMessage _ _ = defaultFormMessage 19 - the "FormApp" web application only provides one route 20 mkYesod "FormApp" [parseRoutes| 21 /contact ContactR GET |] 22 - an auxiliary function integrates the form widget into a web page 23 page enctype widget res = defaultLayout $ do 24 -- set the title of the website 25 setTitle "Contact" 26 [whamlet| 27 <p>Result: #{show res} 28 <form enctype=#{enctype}> 29 ^{widget} 30 <button>Send 31 |] 32 - HTML input field data types 33 data ContactForm = ContactForm { 34 contactName :: Text 35 ,contactEmail :: Text 36 ,contactText :: Textarea 37 } deriving Show 38 - function generates a form of MForm type 39 createForm :: Html -> MForm Handler (FormResult ContactForm, Widget) 40 createForm extra = do 41 -- mreq = required -> required field 42 (nameRes,nameView) <- mreq textField "Name" Nothing 43 (emailRes,mailView) <- mreq emailField "Email" Nothing 44 (textRes,textView) <- mreq textareaField "Text" Nothing 45 let contactRes = ContactForm <$> nameRes <*> emailRes <*> textRes 46 widget = do 47 toWidget 48 -- integrate Lucius stylesheet 49 [lucius| 50 @bColor: mediumseagreen; 51 ##{fvId nameView}, ##{fvId mailView}, ##{fvId textView}{ 52 border: 3px solid #{bColor}; 53 } 54 |] 55 [whamlet| 56 #{extra} 57 <h1>Contact Form! 58 <p>Name: ^{fvInput nameView} 59 <p>Email: ^{fvInput mailView} 60 <p>Text: ^{fvInput textView} 61 |] 62 return (contactRes,widget) 63 getContactR :: Handler Html 64 getContactR = do 65 ((res,widget), enctype) <- runFormGet $ createForm 66 page enctype widget res
The code in Listing 4 asks site visitors for their names and email addresses and prompts them to leave a message (Figure 2). When you access the web page, the getContactR
function (lines 63 and 64) generates the form, and clicking on Send
outputs the previously submitted data.
Buy this article as PDF
(incl. VAT)