We need a new Channel type:
data Channel m = Channel {
send :: LBS.ByteString -> m (),
recv :: STM m (Maybe LBS.ByteString)
}
The Driver type can stay as is. However codec type is not general enough:
data Codec ps failure m bytes = Codec {
encode :: forall (st :: ps) (st' :: ps).
SingI st
=> ActiveState st
=> Message ps st st'
-> bytes,
decode :: forall (st :: ps).
ActiveState st
=> Sing st
-> m (DecodeStep bytes failure m (SomeMessage st))
}
We will need a codec which works in both m and STM m. cborg requires access to the ST operations, e.g. mkCodecCborStrictST but STM monad has no MonadST instance.
In coot/typed-protocols-rewrite branch we have:
runDecoderWithChannel :: MonadSTM m
=> Channel m bytes
-> Maybe bytes
-> DecodeStep bytes failure m a
-> m (Either failure (a, Maybe bytes))
tryRunDecoderWithChannel :: Monad m
=> Channel m bytes
-> Maybe bytes
-> DecodeStep bytes failure m (SomeMessage st)
-> m (Either failure
(Either (DriverState ps pr st bytes failure (Maybe bytes) m)
(SomeMessage st, Maybe bytes)))
because of the above constraint we cannot change its signature to STM m, but we can guarantee that all recvs are non blocking (e.g. atomically $ Just <$> recv `orElse` pure Nothing).
The tryRunDecoderWithChannel one is used to implement the tryRecvMessage record field of Driver. And it is plausible to implement it with recv :: STM m (Maybe ByteString))
data Driver ps (pr :: PeerRole) bytes failure dstate m =
Driver {
...
tryRecvMessage :: forall (st :: ps).
SingI st
=> ActiveState st
=> ReflRelativeAgency (StateAgency st)
TheyHaveAgency
(Relative pr (StateAgency st))
-> DriverState ps pr st bytes failure dstate m
-> m (Either (DriverState ps pr st bytes failure dstate m)
( SomeMessage st
, dstate
))
, -- | Construct a non-blocking stm action which awaits for the
-- message.
--
recvMessageSTM :: forall (st :: ps).
SingI st
=> ActiveState st
=> ReflRelativeAgency (StateAgency st)
TheyHaveAgency
(Relative pr (StateAgency st))
-> DriverState ps pr st bytes failure dstate m
-> m (STM m (SomeMessage st, dstate))
, startDState :: dstate
}
The question is how we can implement recvMessageSTM. For that it seems that being able to run a decoder in the STM monad (without forking a thread) is indispensable.
GHC exposes unsafeIOToSTM which could be used to lift ST to STM (via IO), but this is rather dodgy way, so a different solution is needed. On the other hand, a rudimentary inspection of cborg library shows that ST is deeply grained, e.g.
We need a new
Channeltype:The Driver type can stay as is. However codec type is not general enough:
We will need a codec which works in both
mandSTM m.cborgrequires access to theSToperations, e.g. mkCodecCborStrictST butSTMmonad has noMonadSTinstance.In
coot/typed-protocols-rewritebranch we have:because of the above constraint we cannot change its signature to
STM m, but we can guarantee that allrecvs are non blocking (e.g.atomically $ Just <$> recv `orElse` pure Nothing).The
tryRunDecoderWithChannelone is used to implement thetryRecvMessagerecord field ofDriver. And it is plausible to implement it withrecv :: STM m (Maybe ByteString))The question is how we can implement
recvMessageSTM. For that it seems that being able to run a decoder in theSTMmonad (without forking a thread) is indispensable.GHCexposes unsafeIOToSTM which could be used to liftSTtoSTM(viaIO), but this is rather dodgy way, so a different solution is needed. On the other hand, a rudimentary inspection ofcborglibrary shows thatSTis deeply grained, e.g.