fnhaskell.com/tutorial
data Ctxt = Ctxt FnRequest
instance RequestContext Ctxt where
getRequest (Ctxt req) = req
setRequest (Ctxt oldReq) newReq = Ctxt newReq
Web Application Interface
Common interface for frameworks and libraries
Lots of nice middleware for logging, sessions, etc
the actual server
import Web.Fn
import Network.Wai (Application)
import Network.Wai.Handler.Warp (run)
main :: IO ()
main = run 3000 waiApp
waiApp :: Application
waiApp = toWAI (Ctxt defaultFnRequest) site
run :: Int → Application → IO ()
toWAI :: ctxt → (ctxt → IO Response) → Application
site :: ctxt → IO Response
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH ]
`fallthrough` notFoundText "Page not found."
indexH :: Ctxt -> IO (Maybe Response)
indexH ctxt = okText "Welcome to my first Haskell website."
import Web.Fn
import Network.Wai (Application)
import Network.Wai.Handler.Warp (run)
data Ctxt = Ctxt FnRequest
instance RequestContext Ctxt where
getRequest (Ctxt req) = req
setRequest (Ctxt oldReq) newReq = Ctxt newReq
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH ]
`fallthrough` notFoundText "Page not found."
indexH :: Ctxt -> IO (Maybe Response)
indexH ctxt = okText "Welcome to my first Haskell website."
main :: IO ()
main = run 3000 waiApp
waiApp :: Application
waiApp = toWAI (Ctxt defaultFnRequest) site
import Network.Wai (responseLBS)
import Network.HTTP.Types.Status (status418)
coffeeHandler :: Context -> IO (Maybe Response)
coffeeHandler ctxt =
return $ Just (responseLBS status418
[]
"I'm a teapot not a coffeemaker.")
Lucid
Heist
Blaze
probably many more!!
import Lucid
-- ...
indexView :: Html ()
indexView = do
html_ $ do
head_ $ do
title_ "My fancy Haskell site"
body_ $ do
p_ "Welcome to my FANCY Haskell website"
lucidHtml :: Html () -> IO (Maybe Response)
lucidHtml h = okHtml $ toStrict $ renderText h
Html () -> Data.Text.Lazy
Data.Text.Lazy -> Data.Text.Strict
Data.Text.Strict -> IO (Maybe Response)
indexHandler :: Context -> IO (Maybe Response)
indexHandler ctxt = ????
indexHandler :: Context -> IO (Maybe Response)
indexHandler ctxt = lucidHtml indexView
baseView :: Text -> Html () -> Html ()
baseView title rest =
html_ $ do
head_ $ do
link_ [ href_ "style.css",
rel_ "stylesheet",
type_ "text/css" ]
title_ (toHtml title)
body_ $ rest
indexView :: Html ()
indexView =
baseView "My fancy Haskell site" $
body_ $
p_ "Welcome to my FANCY Haskell website"
routes :: Ctxt -> IO (Maybe Response)
routes ctxt =
route ctxt [ {- maybe matches ==> maybe handles -}
, {- maybe matches ==> maybe handles -}
, {- maybe matches ==> maybe handles -} ]
site :: Ctxt -> IO Response
site = routes ctxt `fallthrough` notFoundText "not found"
maybe a route matches, maybe a handler returns a response
if not: fallthrough to this other response
end path "blah" method "GET" anything
matches when there's nothing left
matches "blah"
matches only GET requests
matches anything!
segment param "blah"
passes a segment to the handler
passes the value of param "blah" to the the handler
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // end ==> helloH
, path "hello" // segment // end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // segment // end ==> helloNameH
, path "hello" ==> rudeHelloH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name =
if name == "dbp"
then return Nothing
else okText ("Hello, " <> name <> "!")
rudeHelloH :: Ctxt -> Text -> IO (Maybe Response)
rudeHelloH ctxt name = okText "Not you again."
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "add" // segment
// segment
// end ==> addNumbersH
, path "add" // segment
// segment
// end ==> addWordsH ]
`fallthrough` notFoundText "Page not found."
Same pattern, but two different handlers?
localhost:6000/add/(segment)/(segment)
addNumbersH :: Ctxt -> Int -> Int -> IO (Maybe Response)
addNumbersH ctxt number1 number2 =
let sum = number1 + number2 in
okText (show number1 <> " plus " <>
show number2 <> " is " <> sum <> ".")
addWordsH :: Ctxt -> Text -> Text -> IO (Maybe Response)
addWordsH ctxt word1 word2 =
okText (word1 <> " plus " <> word2 <>
" is " <> word1 <> word2 <> ".")
localhost:6000/add/1/2
1 plus 2 is 3
localhost:6000/add/ny/haskell
ny plus haskell is nyhaskell
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "add" // param "n1"
// param "n2"
// end ==> addNumbersH
, path "add" // segment
// segment
// end ==> addNumbersH ]
`fallthrough` notFoundText "Page not found."
Different patterns, but same handler
localhost:6000/add?n1=1&n2=2
localhost:6000/add/1/2
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "add" // segment // segment // end ==> addNumbersH
, path "add" // param "n1" // param "n2" ==> addNumbersH
, path "add" // segment // segment // end ==> addWordsH ]
`fallthrough` notFoundText "Page not found."
addNumbersH :: Ctxt -> Int -> Int -> IO (Maybe Response)
addNumbersH ctxt number1 number2 =
let sum = number1 + number2 in
okText (show number1 <> " plus " <>
show number2 <> " is " <> sum <> ".")
addWordsH :: Ctxt -> Text -> Text -> IO (Maybe Response)
addWordsH ctxt word1 word2 =
okText (word1 <> " plus " <> word2 <> " is " <> word1 <> word2 <> ".")