Closed safareli closed 4 years ago
actually here:
, beginBlock :: RequestBeginBlock -> m ResponseBeginBlock
, endBlock :: RequestEndBlock -> m ResponseEndBlock
results here should instead be some monoid so responses from all modules can be combined.
This is starting to look pretty good. Couple of things to think about going forward:
Keeper
someone outside the module, because i see things here like mkUserModule
:: HasCodec User cdc
=> BankKeeper IO
-> IO
( Module UserQueryRoute UserMsgRoute (ReaderT (Store cdc) IO)
, UserKeeper IO
)
where the tuple return type to me means "yes". What about the comparison to Halogen
where the Keeper
is effectively the Halogen.Query a
type, and arguable also the Halogen.Message a
type that the component can raise
even though I'm not sure what raise
would mean here. It seems like the functionality listed here is still covered.
url
style querying is meant for external clients only, and that all inter-module communication is done by simple message passing. This means that it's possible we can omit the queryRoute
parameter from the type of Module
and instead do something like:
type family QueryApi :: Type -> Type
type instance QueryApi BankModule = BankApi
type instance QueryApi UserModule = UserApi
type instance QueryApi RootModule = BankApi :<|> UserApi
server :: RouteT (QueryApi Rootmodule) m
server = bankHandlers :<|> userHandlers
it would make the types much shorter and make it harder for module A
to use the api of module B
even if it had a reference to the type or module
Also just to clear up the example code you've posted, I don't see the need for strings in the *Msg
routes, would they be used for something or can we remove them for clarity
Also with regard the the Begin/EndBlock
monoid instances, probably the return type for these hooks is not actually m Response.BeginBlock
or m Response.EndBlock
but more like m ()
at the module level, in which case there is already monoid instance for a -> m ()
. If the RootModule
is guaranteed that those hooks are run in order, then it can assemble the Response.EndBlock
response to the tendermint-core server using whatever implicit state changes that those hooks made.
There's not a lot of examples of this in their docs, most have to do with updating the modules that govern consensus and voting rights, i.e. token balances
- Are you implying the Keeper someone outside the module, because i see things here like
Yes.
What about the ... Message functionality if needed can be achived by having having something like this
subscribe :: BusW UpdateMsgs -> IO ()
in Keeper
- Just so everyone's on the same page, my understanding is that this url style querying is meant for external clients only, and that all inter-module communication is done by simple message passing.
Yes url style execute & query handlers are for external use. if some functionality is needed from other module Keepers are for that. Keeper is basically "internal-public" api. Also technically we we can hide constructor of the Module.
This means that it's possible we can omit the queryRoute parameter from the type of Module and instead do something like: ...
for writing handlers you would need access to keepers. also handlers will need same things a module would need.
we might do something like this:
mkUserModule
:: HasCodec User cdc
=> BankKeeper IO
-> IO
( Module (ReaderT (Store cdc) IO)
, UserKeeper IO
, RouteHandler UserQueryRoute (ReaderT (Store cdc) IO)
, RouteHandler UserMsgRoute (ReaderT (Store cdc) IO)
)
and assemble handlers by hand.
Also just to clear up the example code you've posted, I don't see the need for strings in the *Msg routes, would they be used for something or can we remove them for clarity
the strings from *Msg, would be used to construct message / query, so given
type BankQueryRoute
= "supply" :-> Token
<|> "getBalance" :/ Address :-> Token
type BankMsgRoute =
"transfer" :? Transfer -> Unit
...
app = do
....
toApp $ hoistModule' (runReaderT store) $ mkRootModule
{ bank: bankM
...
}
then for making transfer one would create message using bank/transfer?from=0x002&to=0x003&amount=0x001
or for querying use this: bank/getBalance/0x002
Also with regard the the Begin/EndBlock monoid instances, probably the return type for these hooks is not actually m Response.BeginBlock or m Response.EndBlock but more like m () at the module level, in which case there is already monoid instance for a -> m (). If the RootModule is guaranteed that those hooks are run in order, then it can assemble the Response.EndBlock response to the tendermint-core server using whatever implicit state changes that those hooks made.
I think this handlers should return some monoid which contains logs, events raised (like etherium) maybe stats like "Gas used" etc. and it should be monoid so this result's then would combine and returned to client / published in block (like Events in etherium). or this could be done using MonadLog Stats m
or something, as we would need to create such logs when handling Messages too.