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.
:i ReaderT
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
What about Ev?... nah.
That is as far down the HAppS type rabbit hole as I am going to go.
Think of it like this: askDatastore is a macid event that reads a value.
newtype ReaderT r m a = ReaderT {runReaderT :: r -> m a}
-- Defined in Control.Monad.Reader
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