Closed coot closed 11 months ago
Another drawback of using custom StateToken
is that for ChainSync
we will still need to use singletons.
data SingNextKind (k :: StNextKind) where
SingCanAwait :: SingNextKind StCanAwait
SingMustReply :: SingNextKind StMustReply
-- we cannot use `StateTokenI` as `SingNextKind` is defined outside of the scope of `Protocol` -- type class.
type instance Sing = SingNextKind
instance SingI StCanAwait where sing = SingCanAwait
instance SingI StMustReply where sing = SingMustReply
instance Protocol (ChainSync header point tip) where
data Message ...
type StateAgency ...
data StateToken (k :: ChainSync header point tip) where
SingIdle :: StateToken StIdle
SingNext :: SingNextKind k
-> StateToken (StNext k)
SingIntersect :: StateToken StIntersect
SingDone :: StateToken StDone
instance StateTokenI StIdle where stateToken = SingIdle
instance SingI k =>
StateTokenI (StNext k) where stateToken = SingNext sing
instance StateTokenI StIntersect where stateToken = SingIntersect
instance StateTokenI StDone where stateToken = SingDone
Furthermore we cannot just use something like withSingI, which implementation. is rather tricky. withSingI
is actually a useful tool, here is a showcase example in ChainSync
's codec.
I think it's better to use SingI
from singletons
, but it means we will also need to use Sing
. We still could include the StateToken
data family as part of the Protocol
class but one will need to provide Sing
and SingI
instances for it outside of the Protocol
class.
Although I don't see how to define Sing
instance, obviously this will overlap with everything:
type instance Sing = StateToken
in some cases this will probably work:
type instance Sing = StateToken @PingPong
but this will not:
type instance Sing = StateToken @(ReqResp req resp) -- req and resp are not defined.
So we're left with the singletons defined out of the scope of Protocol
type class.
This leads me to the following idea:
class Protocol ps where
data Message ps (st :: ps) (st' :: ps)
type StateAgency (st :: ps) :: Agency
-- | A protocol state token, e.g. term level representation of type level
-- state (also known as singleton).
--
type StateToken :: ps -> Type
-- | An alias for 'sing'.
--
stateToken :: (SingI st, Sing st ~ StateToken st) => StateToken st
stateToken = sing
Protocol
type class requires all data types (Message
and StateToken
) and the type StateAgency
type familySing
and SingI
instances as constraints, although they must be provided.withSingI
and other singletons
goodies.
We can have something like
It would be nice to be able to declare something like:
But the problem is that there's no nice way to provide an evidence for the
QuantifiedConstraint
.StateTokenI
instance must be defined per state, not in bulk, unless we do something ugly like: