Providence Salumu
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.
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 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);
}
$ 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.
$ 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
$ 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.
$ 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
$ 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
$ 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
$ 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
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.
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!')
$ 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
Hopefully you feel for what exists in the ecosystem and feel slightly more empowered to use the amazing tools we have.