1. happs intro
  2. the missing happs documentation
  3. getting started with happs
  4. prerequisites
  5. cabal install me
  6. main
  7. url handling
  8. templates
  9. stringtemplate basics
  10. debugging
  11. form data: get and post
  12. form data: file uploads
  13. cookies
  14. introduction to macid
  15. using macid safely
  16. macid dummy data
  17. macid updates and queries
  18. changing the data model
  19. macid stress test
  20. limitations of macid
  21. foreign characters
  22. cron jobs
  23. thanks
  24. appendix (floundering in ghci)

Where it all begins: the function main

Have a look at the Main.hs module which is at the core of this web application.

In particular, notice the line

smartserver (Conf p Nothing) "happs-tutorial" (controller tDirGroups dynamicTemplateReload allowStressTests) stateProxy

This is a library function, which can be looked up via the ghci info directive (or i for short):

*Main> :i smartserver
smartserver ::
(Methods st, Component st, ToMessage a) =>
Conf -> String -> [ServerPartT IO a] -> Proxy st -> IO ()
-- Defined in HAppS.Server.Helpers

Each of these arguments is important enough to say something brief about.

In this case, the configuration argument just specifies the port the server runs on.

The second string argument controls where serialized app state will be stored: in this case, under "_local/happs-tutorial". This is mainly for convenience, so the state directory is the same whether we are running in ghci or ghc. (HAppS out of the box just looks at the executable name, which in the case of ghci is something weird.)

The third argument tells HAppS what to use as a controller, in the MVC sense. Basically, how to handle http requests. If dynamicTemplateReload is true, HAppS reloads reloads the templates from the template directory on every request, otherwise it just does this once at app startup. The former is nice for development because you can change content on the fly, the latter is better when you want to maximize server performance uner heavy load and the content has stabilized.

The fourth argument tells happs what data structure to use for application state. We'll cover about the HAppS state system in depth, but later.

To learn more about what the controller function is doing, , open this file in ghci: cd src; ghci Main.hs and have a look at these functions using ghci :info .


*Main> :i controller
controller :: Bool -> [ServerPartT IO Response]
-- Defined at Controller.hs...

*Main> :i ServerPartT
newtype ServerPartT m a = ServerPartT {unServerPartT :: Request -> WebT m a}
-- Defined in HAppS.Server.SimpleHTTP
instance [overlap ok] (Monad m) => Monad (ServerPartT m)
-- Defined in HAppS.Server.SimpleHTTP

*Main> :i WebT
newtype WebT m a = WebT {unWebT :: m (Result a)}
-- Defined in HAppS.Server.SimpleHTTP
instance [overlap ok] (Monad m) => Monad (WebT m)
-- Defined in HAppS.Server.SimpleHTTP

*Main> :i Result
data Result a
= NoHandle | Ok (Response -> Response) a | Escape Response
-- Defined in HAppS.Server.SimpleHTTP
instance [overlap ok] (Show a) => Show (Result a)
-- Defined in HAppS.Server.SimpleHTTP

The controller function is a list of ServerPartTs, which are handlers that accept an HTTP request and return a response. (The boolean argument determines whether or not we enable a certain handler -- "stress tests" -- which is discussed later; don't worry about it for now.) This is a bit obfuscated by the many types involved in the construction, and if you want to be pedantic it's actually a bit more complicated than that, but you don't need to understand all the details at this point. So, for the moment, just think about a ServerPartT as a wrapper over a function that takes an HTTP request and returns a response. We look at what is going on in the controller code in basic url handling, next.