I recently started playing with Haskell's Warp in my effort to learn Haskell. Warp is small and fast web server, and doesn't come bundled with much. It also has no "magic" in it, which I think is a very good thing.
My hello-world program for any web server, is to respond with {"hello":"world"}
. Here it is:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
module Main where
import Network.Wai (responseLBS, Application, Response, rawPathInfo)
import Network.Wai.Handler.Warp (run)
import Network.HTTP.Types (status200, status404)
import Network.HTTP.Types.Header (hContentType)
import Data.Aeson
import GHC.Generics
data Hello = Hello { hello :: String } deriving (Generic, ToJSON)
main :: IO ()
main = do
let port = 3000
putStrLn $ "Listening on port " ++ show port
run port app
app :: Application
app req res =
res $ case rawPathInfo req of
"/" -> helloRoute
_ -> notFoundRoute
helloRoute :: Response
helloRoute =
responseLBS
status200
[(hContentType, "application/json")]
. encode $ Hello "World"
notFoundRoute :: Response
notFoundRoute =
responseLBS
status404
[(hContentType, "application/json")]
"404 - Not Found"
As you can see, the code has a main
function that runs the app
. This app
just matches on routes, and responds with HTTP 200 OK
on the root, or HTTP 404 Not Found
. These are returned by different functions which returning Response
types, and contain most of the hard parts of this code.
Talking of hard parts, the thing I had the most difficultly with was:
Working out what responseLBS
meant, and how to use it
responseLBS
means "respond with a LazyByteString". LazyByteString seems to be the standard way of dealing with Strings in Warp. Their laziness, and serialisation format, apparently yields very high performance.
However, using them is not so simple. Unless you convert from a normal String to a LazyByteString, you receive this compiler error:
Couldn't match expected type
‘bytestring-0.10.8.1:Data.ByteString.Internal.ByteString’
with actual type ‘[Char]’
The OverloadedStrings
pragma on the first line makes responseLBS usable, but providing a typeclass that automatically converts Strings to LazyByteStrings. It took me a while to realise this.
Summary
One of the best things about this code was the experience writing it. Once it compiled, it worked. It took a while to work out, but it was all helpfully guided by the compiler.
I think I'd probably benefit from an IDE for Haskell, as I spend much of my time in IntelliJ coding Scala. Discovering the necessary imports was the trickiest part of coding in the Atom Editor. If you have any recommendations, please send them my way!
IDE's aside, if coding in Haskell is always this pleasant, I'll be very pleased!