When working with HAppS, 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 HAppS-State-0.9.2.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 HAppS type rabbit hole as I am going to go.