1. new in this version
  2. build a web 2.0 app in happstack
  3. why happstack is cool
  4. getting started with happstack
  5. prerequisites
  6. cabal install me
  7. first shot at happstack
  8. url handling
  9. basic HTML inclusion
  10. templates
  11. stringtemplate basics
  12. debugging
  13. form data: get and post
  14. form data: file uploads
  15. cookies
  16. introduction to macid
  17. first steps with macid
  18. scaling with multimaster
  19. using macid safely
  20. macid dummy data
  21. changing the data model
  22. macid stress test
  23. limitations of macid
  24. foreign characters
  25. IxSets
  26. cron jobs
  27. thanks
  28. appendix (floundering in ghci)

Appendix: Using ghci with Happstack

When working with Happstack, I seem to spend a lot of time in ghci looking at type signatures, and it's not always obvious what's going on. So I thought I'd share a little mini ghci session in case this helps anyone.

*Main> :t query AskDatastore
query AskDatastore :: (Control.Monad.Trans.MonadIO m) => m (Data.Set.Set User)
*Main> :t query AskDatastore :: WebT IO (Data.Set.Set User) -- concretize the type
query AskDatastore :: WebT IO (Data.Set.Set User) :: WebT IO (Data.Set.Set User) -- hoorah, it typechecks

*Main> :t update InitializeDummyData
update InitializeDummyData :: (Control.Monad.Trans.MonadIO m) => m ()
*Main> :t update InitializeDummyData :: WebT IO () -- concretize the type
update InitializeDummyData :: WebT IO () :: WebT IO ()

What you see above are nice looking types. And they can be made concrete, with WebT -- no class class context in the type signature. This is Right.

What happens if we try an update on something that should really be a query? Does it get rejected?

*Main> :t update AskDatastore
update AskDatastore :: (UpdateEvent AskDatastore res, Control.Monad.Trans.MonadIO m) => m res
*Main> :t query InitializeDummyData
query InitializeDummyData :: (QueryEvent InitializeDummyData res, Control.Monad.Trans.MonadIO m) => m res

Typechecks with a weird class context, but will complain if run, and impossible to make concrete in ghci using :: to narrow the type. Ugly looking types. This is Wrong.


What exactly is askDatastore?

*Main> :i askDatastore
askDatastore :: Query AppState (Data.Set.Set User)
-- Defined at src/StateVersions/AppState1.hs

what's a Query?

Prelude Control.Monad.Reader HAppS.State GHC.Conc> :i Query
type Query state = Ev (ReaderT state STM)
-- Defined in Happstack-state-0.1:HAppS.State.Types

data StateStuff.Method st where
...
Query :: forall st ev res.
(QueryEvent ev res) =>
(ev -> Query st res) -> StateStuff.Method st
-- Defined in HAppS.State.ComponentSystem

that's weird. in the type synonym Query takes a single arg, but in the definition for askDatastore, it took two. I guess the type system uses currying. Let's just substitute the concrete type AppState for the type variables state and see if this mess typechecks.

*StateVersions.AppState1 Control.Monad.Reader GHC.Conc Data.Set> :t askDatastore :: Ev (ReaderT AppState STM) (Set User)
askDatastore :: Ev (ReaderT AppState STM) (Set User) :: Ev (ReaderT AppState STM) (Set User)
*Main>

No type errors -- it works.
Think of it like this: askDatastore is a macid event that reads a value.

:i ReaderT
newtype ReaderT r m a = ReaderT {runReaderT :: r -> m a}
-- Defined in Control.Monad.Reader

The type constructor ReaderT takes three arguments to yield a concrete type. In the definition for askDatastore, it is partially applied, with two arguments. ReaderT AppState STM is an instance of Monad, and also an instance of MonadReader AppState, with AppState as the reader environment and STM (software transactional memory) as the wrapped monad.

*Main Control.Monad.Reader HAppS.State GHC.Conc> :i ReaderT
instance (Monad m) => Monad (ReaderT r m)
&nbs; *Main Control.Monad.Reader HAppS.State GHC.Conc> :i ReaderT -- Defined in Control.Monad.Reader
instance (Monad m) => MonadReader r (ReaderT r m)
   -- Defined in Control.Monad.Reader

What about Ev?... nah.

That is as far down the Happstack type rabbit hole as I am going to go.