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
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" // segment // end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")
helloNameH handles "/hello/party"
"Hello, party!"
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 "hello" // param "name" //
end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")
helloNameH handles "/hello?name=party"
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // segment // end ==> helloNameH
, path "hello" // param "name" //
end ==> helloNameH ]
`fallthrough` notFoundText "Page not found."
helloNameH :: Ctxt -> Text -> IO (Maybe Response)
helloNameH ctxt name = okText ("Hello, " <> name <> "!")
helloNameH handles "/hello/party" and "/hello?name=party"
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/monadic/party
monadic plus party is monadic party
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."
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 <> ".")