Providence Salumu Haskell for Web Developers

Haskell for Web Developers

This blog post is somewhat dated and may not reflect changes to the ecosystem since 2013.

The perpetual myth persists that Haskell cannot be used for “real world applications”. Normally real world is usually left undefined in such a discussion, but can often be taken to mean that Haskell is not suited for database and web development work.

Haskell has a rich library ecosystem and is well-suited for these tasks but I concede that there might be a systemic lack of introductory material for many domain specific tasks. Something that many projects and companies are trying to remedy.

Haskell does indeed have several great web frameworks along the lines of RoR, Django, Flask, Pyramid etc.

  1. Yesod
  2. Snap
  3. Happstack

I will not discuss these though because I really couldn’t give a better introduction than their own documentation. Instead I will focus on simple motivating examples for smaller libraries which provide a rich feature base for web development tasks while leveraging the strengths of Haskell language itself, and many of which can integrate with the larger frameworks.

Clay

Clay is a library for programmatic generation of CSS. Is it an embedded DSL (EDSL) that exposes selectors and styles for the CSS3 grammmer. Clay is designed to layer logic on top of the CSS as to encode variables, color mixing, complex selector logic and nested rules more easily than with base CSS. Clay can also be usefull as a lower-level combinator library to describe complex CSS layouts.

$ cabal install clay
{-# LANGUAGE OverloadedStrings #-}

import Clay
import Data.Text
import Prelude hiding (div)

bodyStyle :: Css
bodyStyle = body ? do
  background  aquamarine
  fontFamily  ["Helvetica Neue"] [sansSerif]

codeStyle :: Css
codeStyle = code ?
  do fontFamily  ["Monaco", "Inconsolata"] [monospace]
     fontSize    (px 14)
     lineHeight  (ex 1.8)

emphasis :: Css
emphasis = do 
  fontWeight     bold
  color          black
  textTransform  uppercase

container :: Selector
container = div # ".code"

containerStyle :: Css
containerStyle = container ?
  do width (px 800)
     borderColor gray

main :: IO ()
main = putCss $ do
  bodyStyle
  codeStyle
  containerStyle

The above will generate the following stylesheet:

body
{
  background  : rgb(127,255,212);
  font-family : "Helvetica Neue", sans-serif;
}

code
{
  font-family : "Monaco","Inconsolata", monospace;
  font-size   : 14px;
  line-height : 1.80000ex;
}

div.code
{
  width        : 800px;
  border-color : rgb(128,128,128);
}

Blaze

$ cabal install blaze-html

Blaze is the bread and butter of markup generation in Haskell. It is described as a “blazingly fast HTML combinator library” which programmtically generates HTML and several other markup languages from an embedded DSL.

In this module, the language extension OverloadedStrings is used so that the type inferencer can infer common coercions between String-like types without having to do explicit calls to boilerplate functions (pack, unpack, html) for each string-like literal. This will be pretty common use for all the examples from here out that use ByteString or HTML.

{-# LANGUAGE OverloadedStrings #-}

import Data.Monoid
import Text.Blaze (ToMarkup(..))
import Text.Blaze.Html5 hiding (html, param)
import Text.Blaze.Html.Renderer.Text (renderHtml)
import qualified Text.Blaze.Html5 as H

gen :: Html -> [Html] -> Html
gen title elts  = H.html $ do
  H.head $
    H.title title
  H.body $
    H.ul $ mapM_ H.li elts

main :: IO ()
main = do
  print $ renderHtml $ gen "My Blog" ["foo", "bar", "fizz"]

This would output HTML like the following:

<html>
   <head>
      <title>My Blog</title>
   </head>
   <body>
      <ul>
          <li>foo</li>
          <li>bar</li>
          <li>fizz</li>
      </ul>
   </body>
</html>

In addition to generating HTML we can also derive from it’s internal ToMarkup classes to provide HTML representations for any datatype in Haskell. A silly example might be:

data Example = A | B deriving (Show)
data List a = Cons a | Nil deriving (Show)

instance ToMarkup Animal where
  toMarkup = toHtml . show

instance (ToMarkup a) => ToMarkup (List a) where
  toMarkup x = case x of
      Cons a -> H.ul $ H.li $ toHtml a
      Nil -> ""
<!-- Cons (Cons (Cons A)) -->
<ul>
   <li>
      <ul>
         <li>
            <ul>
               <li>A</li>
            </ul>
         </li>
      </ul>
   </li>
</ul>

It is worth noting that the Blaze builder overloads do-notation as some EDSLs do, but the Html type is not a monad. It is functionally a monoid.

For non-embedded template languages along the lines of Jinja or erb refer to the Shakespearean templates or heist.

JMacro

$ cabal install jmacro

JMacro is quasiquoter for Javascript code generation. The underlying implementation is rather clever and allows Haskell and Javascript to share functions and values across quotation boundaries. The end result is a fusion of Haskell and JavaScript that serves as a foundation to higher abstractions and as a very convienant way to implement code generation for compilers targetting Javascript.

As an example of we’ll use JMacro to implement a simple translator for the untyped typed lambda calculus, something one might do if writing a language that transpiles to Javascript.

{-# LANGUAGE QuasiQuotes, TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}

import Data.String
import Language.Javascript.JMacro

jref :: Sym -> JExpr
jref = ValExpr . JVar . StrI

jvar :: Sym -> JStat
jvar sym = DeclStat (StrI sym) Nothing

jprint x = [jmacroE|console.log(x)|]

instance IsString Expr where
  fromString x = Var (Sym x)

data Val = Sym Sym
         | Lit Lit
         deriving (Show)

type Sym = String

data Lit = LStr String
         | LInt Int
         deriving (Show)

data Expr = App Expr Expr
          | Lam Sym Expr
          | Var Val
          deriving (Show)

-- Convert Haskell expressions to Javascript expressions

instance ToJExpr Val where
  toJExpr (Sym s) = toJExpr s
  toJExpr (Lit l) = toJExpr l

instance ToJExpr Lit where
  toJExpr = toJExpr

instance ToJExpr Sym where
  toJExpr = jref

instance ToJExpr Expr where
  toJExpr (Lam s ex) = 
      [jmacroE| 
          function(arg) {
              `(jvar s)`;
              `(jref s)` = `(arg)`;
              return `(ex)`;
          }
      |]

  toJExpr (App f x) = 
      [jmacroE| `(f)`(`(x)`) |]

  toJExpr (Var v) =
      toJExpr v

compile :: ToJExpr a => a -> String
compile = show . renderJs . toJExpr

s, k, i0, i1 :: Expr
s = Lam "f" $ Lam "g" $ Lam "x" $ (App "f" "x") `App` (App "g" "x")
k = Lam "x" $ Lam "y" "x"

i0 = Lam "x" "x"
i1 = App (App s k) k

main :: IO ()
main = do
  putStrLn $ compile s
  putStrLn $ compile k
  putStrLn $ compile i0
  putStrLn $ compile i1

Fay

$ cabal install fay

Fay is a growing ecosystem of packages that can compile Haskell to Javascript. Fay works with a strict subset of Haskell that preserves Haskell semantics such as currying and laziness. In addition to the core language, ther are interfaces for jquery and DOM manipulation so that Fay-compiled Haskell code can effectively access the browser internals.

$ cabal install fay-dom fay-jquery

The code generation is rather verbose given that it compiles quite a bit of the Haskell Prelude. The below example is very simple and only the interesting part of the outputted source is shown below. Notably the generated code is very readable.

-- demo.hs
import FFI
import Prelude
import JQuery

puts :: String -> Fay ()
puts = ffi "console.log(%1)"

example = take 25 [1..]

main :: Fay ()
main = ready $ do
    el <- select "#mydiv"
    setCss "background-color" "red" el

    puts "Hello World!"
    puts (show [1,2,3])

To compile invoke the compiler:

$ fay demo.hs --package fay-jquery

Some of the generated code:


var Prelude$enumFrom = function ($p1) {
    return new Fay$$$(function () {
        var i = $p1;
        return Fay$$_(Fay$$_(
            Fay$$cons)(i))(Fay$$_(
            Prelude$enumFrom)(
            Fay$$_(Fay$$_(
                Fay$$add)(i))(1)
        ));
    });
};

var Main$example = new Fay$$$(
    function () {
        return Fay$$_(Fay$$_(
            Prelude$take)(25))(
            Prelude$enumFrom(1));
    });

To call this code from vanilla Javascript:

var main = new Main();
main._(main.Main$main);

Fay is part of a larger community of compilers that transpile functional languages to Javascript. Another library of note is Roy. Although not Haskell, it has a sophisticated type system and notably an implementation of typeclasses, a feature that Fay currently does not implement.

Aeson

$ cabal install aeson

Aeson is the de-facto JSON parsing and generation library for Haskell. It’s usage couldn’t be simpler, we simply declare instance of toJSON and fromJSON for our types and Aeson takes care of the mappings and exception handling. By using DeriveGeneric we can create instances with very little code.

{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}

import Data.Aeson

import GHC.Generics
import Network.HTTP 
import Control.Applicative
import Data.ByteString (ByteString)

data Message = Message { 
      text :: ByteString
    , date :: ByteString
    } deriving (Show, Generic)

instance FromJSON Message
instance ToJSON Message

fromStdin :: IO (Either String Message)
fromStdin = eitherDecode <$> readLn

postgres-simple

$ cabal install postgres-simple

Postgres-simple is a library for communicating with Postgres databases and mapping data between Haskell and SQL types. Although not an ORM, postgres-simple lets us generate and execute SQL queries and map result sets onto our algebraic datatypes very simply by deriving instances to declare schemas.

{-# LANGUAGE OverloadedStrings #-}

import Data.Text
import Control.Applicative
import Control.Monad
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.FromRow

import qualified Data.ByteString as B
import qualified Database.PostgreSQL.Simple as Pg

data Client = Client { firstName :: Text
                     , lastName :: Text
                     , clientLocation :: Location
                     }

data Location = Location { address :: Text
                         , location :: Text
                         }

instance Pg.FromRow Location where
  fromRow = Location <$> field <*> field

instance Pg.FromRow Client where
  fromRow = Client <$> field <*> field <*> liftM2 Location field field

queryClients :: Connection -> IO [Client]
queryClients c = query_ c "SELECT firstname, lastname, location FROM clients"

main :: IO [Client]
main = do
  uri <- B.getLine
  conn <- connectPostgreSQL uri
  queryClients conn

Acid-State

$ cabal install acid-state

We can further exploit Haskell’s algebraic datatypes to give us a storage engine simply from the specification of our types and a little bit of TemplateHaskell usage. For instance, a simple Map container from Data.Map can be transformed to a disk-backed disk backed key-value store which can be interacted with as if it were a normal Haksell data structure.

type Key = String
type Value = String
data Database = Database !(Map.Map Key Value)

Like it’s name implies acid-state provides transactional guarantees for storage. Specifically that writes will be applied completely or not at all and that data will be consistent during reads.

{-# LANGUAGE OverloadedStrings, TypeFamilies, DeriveDataTypeable, TemplateHaskell #-}

import Data.Acid
import Data.Typeable
import Data.SafeCopy
import Control.Monad.Reader (ask)

import qualified Data.Map as Map
import qualified Control.Monad.State as S

type Key = String
type Value = String

data Database = Database !(Map.Map Key Value)
    deriving (Show, Ord, Eq, Typeable)

$(deriveSafeCopy 0 'base ''Database)

insertKey :: Key -> Value -> Update Database ()
insertKey key value
    = do Database m <- S.get
         S.put (Database (Map.insert key value m))

lookupKey :: Key -> Query Database (Maybe Value)
lookupKey key
    = do Database m <- ask
         return (Map.lookup key m)

deleteKey :: Key -> Update Database ()
deleteKey key
    = do Database m <- S.get
         S.put (Database (Map.delete key m))

allKeys :: Int -> Query Database [(Key, Value)]
allKeys limit
    = do Database m <- ask
         return $ take limit (Map.toList m)

$(makeAcidic ''Database ['insertKey, 'lookupKey, 'allKeys, 'deleteKey])

fixtures :: Map.Map String String
fixtures = Map.empty

test ::  Key -> Value -> IO ()
test key val = do
    database <- openLocalStateFrom "db/" (Database fixtures)
    result <- update database (InsertKey key val)
    result <- query database (AllKeys 10)
    print result

Digestive Functors

$ cabal install digestive-functors digestive-functors-blaze

Digestive functors solve the very mundane but mechanical task of validating forms. The library provides a way to specify views and validation logic and handle the control flow of validation between the end-user and the server. There are several backends to render the form and handle request/response cycles depending on your choice of framework For arbitrary reasons we’ll choose Happstack for this example.

$ cabal install digestive-functors-happstack

We’ll build a simple signup page with username and email validation logic.

{-# LANGUAGE OverloadedStrings #-}

import Data.Maybe
import Text.Printf
import Control.Applicative
import Data.Text (Text, find, splitOn)

import Text.Digestive
import Text.Digestive.Happstack
import Text.Digestive.Blaze.Html5
import qualified Text.Blaze.Html5 as H

import qualified Happstack.Server as HS

data User = User
    { userName :: Text
    , userMail :: Text
    } deriving (Show)

userForm :: Monad m => Form Text m User
userForm = User
  <$> "name" .: check "Name must be two words" checkName (text Nothing)
  <*> "email" .: check "Not a valid email address" checkEmail (text Nothing)

checkEmail :: Text -> Bool
checkEmail = isJust . find (== '@')

checkName :: Text -> Bool
checkName s = length (splitOn " " s) == 2

signupView :: View H.Html -> H.Html
signupView view = form view "/" $ do

    label     "name" view "Full Name:"
    inputText "name" view
    H.br

    label     "email" view "Email:"
    inputText "email" view
    H.br

    childErrorList "" view

    inputSubmit "Signup"

template :: H.Html -> H.Html
template body = H.docTypeHtml $ do
    H.head $ H.title "Example form:"
    H.body body

reply m = HS.ok $ HS.toResponse $ template m

page :: HS.ServerPart HS.Response
page = do
  HS.decodeBody $ HS.defaultBodyPolicy "/tmp/" 0 40960 40960
  r <- runForm "test" userForm
  case r of

      (view, Nothing) -> do
          let view' = fmap H.toHtml view
          reply $ form view' "/" (signupView view')

      (_, Just response) ->
          reply $ do
            H.h1 "Form is valid."
            H.p $ H.toHtml $ show response

config :: HS.Conf
config = HS.nullConf { HS.port = 5000 }

main :: IO ()
main = do
  printf "Listening on port %d\n" (HS.port config)
  HS.simpleHTTP config page

Servers

A great deal of effort has been put into making the Haskell runtime implement efficient event driven programming such that applications can take advantage of the Haskell threading support.

A simple single-threaded Hello World might be written like the following:

{-# LANGUAGE OverloadedStrings #-}

import Network
import Data.ByteString.Char8
import System.IO (hClose, hSetBuffering, Handle, BufferMode(LineBuffering))

msg = "HTTP/1.0 200 OK\r\nContent-Length: 12\r\n\r\nHello World!\r\n"
 
handleClient :: Handle -> IO ()
handleClient handle = do
  hSetBuffering handle LineBuffering
  hGetLine handle
  hPutStrLn handle msg
  hClose handle

listenLoop :: Socket -> IO ()
listenLoop asock = do
  (handle, _, _) <- accept asock
  handleClient handle
  listenLoop asock
 
main ::  IO ()
main = withSocketsDo $ do
  sock <- listenOn $ PortNumber 5000
  listenLoop sock

To make this concurrent we use the function forkIO which utilizes the event-driven IO manager in GHC’s runtime system to spawn lightweight user threads which are distributed across multiple system threads. When compiled with -threaded the Haskell standard library also will use non-blocking system calls which are scheduled by the IO manager ( with epoll() under the hood ) and can transparently switch to threaded scheduling for other blocking operations.

{-# LANGUAGE OverloadedStrings #-}

import Network
import Control.Monad
import Control.Concurrent
import Data.ByteString.Char8
import GHC.Conc (numCapabilities)
import System.IO (hClose, hSetBuffering, Handle, BufferMode(LineBuffering))

numCores = numCapabilities - 1

msg = "HTTP/1.0 200 OK\r\nContent-Length: 12\r\n\r\nHello World!\r\n"
 
handleClient :: Handle -> IO ()
handleClient handle = do
  hSetBuffering handle LineBuffering
  hGetLine handle
  hPutStrLn handle msg
  hClose handle

listenLoop :: Socket -> IO ()
listenLoop asock = do
  (handle, _, _) <- accept asock
  forkIO (handleClient handle)
  listenLoop asock
 
main ::  IO ()
main = withSocketsDo $ do
  sock <- listenOn $ PortNumber 5000
  forM_ [0..numCores] $ \n ->
    forkOn n (listenLoop sock)
  threadDelay maxBound

This example is admittedly very simple but does illustrate that we can switch from serial to concurrent code in Haskell while still preserving sequential logic. Notably this server isn’t really doing anything terribly clever to get performance, it’s simply just spawning threads and all the heavy lifting is handled by the RTS. Yet with only a three line change the server can utilize all available cores.

Compiling with -O2 and running with +RTS -N4 -qm -qa I get the following numbers on my Intel Core i5:

Requests per second:    12446.25 [#/sec] (mean)

There are other Haskell servers which do much more clever things such as the Warp server.

Websockets

The Warp server can utilize the async event notification system to implement asynchronous applications using Control.Concurrent primitives. The prime example is so called “realtime web programming” using websockets. In this example we’ll implement a chat room with a mutable MVar which synchronizes messages across all threads in the server and broadcasts messages to the clients.

$ cabal install wai-websockets
{-# LANGUAGE OverloadedStrings #-}

import Control.Monad
import Text.Printf
import Data.Text (Text)
import Control.Concurrent
import Control.Monad.IO.Class (liftIO)

import Data.Aeson
import qualified Data.Text as T
import qualified Data.Text.IO as T

import qualified Network.Wai
import qualified Network.WebSockets as WS
import qualified Network.Wai.Handler.Warp as Warp
import qualified Network.Wai.Handler.WebSockets as WaiWS
import Network.Wai.Application.Static (defaultFileServerSettings, staticApp)

type Msg = Text
type Room = [Client]
type Client = (Text, WS.Sink WS.Hybi00)

broadcast :: Msg -> Room -> IO ()
broadcast message clients = do
  T.putStrLn message
  forM_ clients $ \(_, sock) -> WS.sendSink sock $ WS.textData message

app :: MVar Room -> WS.Request -> WS.WebSockets WS.Hybi00 ()
app state req = do
  WS.acceptRequest req
  sock <- WS.getSink
  msg <- WS.receiveData
  userHandler msg sock

  where
    userHandler msg sock = do
      let client = (msg, sock)
      liftIO $ T.putStrLn msg
      liftIO $ modifyMVar_ state $ \s -> do
          let s' = client : s
          WS.sendSink sock $ WS.textData $
              encode $ map fst s
          return s'
      userLoop state client

userLoop :: WS.Protocol p => MVar Room -> Client -> WS.WebSockets p ()
userLoop state client = forever $ do
  msg <- WS.receiveData
  liftIO $ readMVar state >>= broadcast (T.concat [fst client, " : ", msg])

staticContent :: Network.Wai.Application
staticContent = staticApp $ defaultFileServerSettings "."

config :: Int -> MVar Room -> Warp.Settings
config port state = Warp.defaultSettings
    { Warp.settingsPort = port
    , Warp.settingsIntercept = WaiWS.intercept (app state)
    }

port :: Int
port = 5000

main :: IO ()
main = do
  state <- newMVar []
  printf "Starting server on port %d\n" port
  Warp.runSettings (config 5000 state) staticContent

In the browser we can connet to our server using Javascript:

ws = new WebSocket('ws://localhost:5000')

ws.onmessage(function(msg){console.log(msg)});
ws.send('User271828')
ws.send('My message!')

Cloud Haskell

$ cabal install distributed-process distributed-process-simplelocalnet

One of the most exciting projects in Haskell is a collections of projects developed under the Cloud Haskell metaproject. Cloud haskell brings language integrated messaging passing capability to Haskell under a very simple API which provides a foundation to build all sorts of distributed computations on top of simple actor primitives.

The core mechanism of action is a Process monad which encapsulates a actor-like computation that can exchange messages across an abstract network backend. On top of this the distributed-process library provides the language-integrated ability to send arbitrary Haskell functions back and forth between processes much like one can move code in Erlang, but while still persiving Haskell type-safety across the message layer. The signatures for the messaging functions are:

send :: Serializable a => ProcessId -> a -> Process ()
expect :: forall a. Serializable a => Process a

The network backend is an abstract protocol that specific libraries ( i.e. distributed-process-simplelocalnet) can implement to provide the transport layer indepenent of the rest of the stack. Many other protocols like TCP, IPC, and ZeroMQ can be used for the network transport.

The simplest possible example is a simple ping and pong between between several Process. Notably we don’t encode any mechanism for binary serialization of code or data since Haskell can derive these for us.

{-# LANGUAGE TemplateHaskell, DeriveDataTypeable, DeriveGeneric, GeneralizedNewtypeDeriving #-}

import Text.Printf
import Data.Binary
import Data.Typeable
import Control.Monad
import System.Environment (getArgs)
import Control.Concurrent (threadDelay)

import Control.Distributed.Process
import Control.Distributed.Process.Closure
import Control.Distributed.Process.Backend.SimpleLocalnet
import Control.Distributed.Process.Node (initRemoteTable,)

newtype Message = Ping ProcessId deriving (Eq, Ord, Binary, Typeable)

pingLoop :: Process ()
pingLoop = do
  liftIO $ putStrLn "Connected with master node."
  forever $ do
    (Ping remote_pid) <- expect
    say $ printf "Ping from %s" (show remote_pid)

    local_pid <- getSelfPid
    send remote_pid (Ping local_pid)

    liftIO $ putStrLn "Pong!"

remotable [ 'pingLoop ]

master :: [NodeId] -> Process ()
master peers = do
  pids <- forM peers $ \nid -> do
      say $ printf "Executing remote function on %s" (show nid)
      spawn nid $(mkStaticClosure 'pingLoop)

  local_pid <- getSelfPid

  forever $ do
    forM_ pids $ \pid -> do
      say $ printf "Pinging remote node %s" (show pid)
      send pid (Ping local_pid)

    forM_ pids $ \_ -> do
      (Ping pid) <- expect
      say $ printf "Received pong from %s"  (show pid)

    liftIO $ threadDelay 1000000

main :: IO ()
main = do
  args <- getArgs

  let host = "localhost"
  let rtable = Main.__remoteTable initRemoteTable

  case args of
    ["master", port] -> do
      printf "Starting master on %s:%s\n" host port
      ctx <- initializeBackend host port rtable
      startMaster ctx master

    ["worker", port] -> do
      printf "Starting client on %s:%s\n" host port

      ctx <- initializeBackend host port rtable
      startSlave ctx 

    otherwise -> error "Invalid arguments: master|worker <port>"

We can then spawn any number of instances from the shell:

$ runhaskell cloud.hs worker 5001
$ runhaskell cloud.hs worker 5002
$ runhaskell cloud.hs master 5003

Conclusion

Hopefully you feel for what exists in the ecosystem and feel slightly more empowered to use the amazing tools we have.