Code Monkey home page Code Monkey logo

typed-protocols's Introduction

Haskell CI handbook

typed-protocols

A robust session type framework which supports protocol pipelining.

Public content

Duncan Coutts (@dcoutts) Haskell eXchange 2019 talk.

Monadic party workshop by Marcin Szamotulski (@coot):

Talk at Haskell Love 2021 by Marcin Szamotulski (@coot), slides.

An Agda implementation by Marcin Szamotulski (@coot).

typed-protocols's People

Contributors

andreabedini avatar avieth avatar coot avatar dcoutts avatar deepfire avatar edsko avatar erikd avatar intricate avatar iohk-bors[bot] avatar jasagredo avatar karknu avatar marcfontaine avatar mrbliss avatar nc6 avatar newhoggy avatar ruhatch avatar tdammers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

typed-protocols's Issues

Experiment with a custom `Sing` type family and `SingI` instances.

We can have something like

class Protocol ps where

  -- | The messages for this protocol. It is expected to be a GADT that is
  -- indexed by the @from@ and @to@ protocol states. That is the protocol state
  -- the message transitions from, and the protocol state it transitions into.
  -- These are the edges of the protocol state transition system.
  --
  data Message ps (st :: ps) (st' :: ps)

  -- | Associate an 'Agency' for each state.
  --
  type StateAgency (st :: ps) :: Agency

  -- | A protocol state token, i.e. a term level representation of type level
  -- state (also known as a singleton).
  --
  type StateToken :: ps -> Type

-- | An association from type level states to term level 'StateToken's.
--
class StateTokenI (st :: ps) where
  stateToken :: StateToken st

It would be nice to be able to declare something like:

-- using `QuantifiedConstraints`
class (forall (st :: ps). StateTokenI st) => Protocol ps where ...

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:

data PingPong = Idle | Busy | Done
  deriving Typeable

data SingPingPong (st :: PingPong) where
  SingIdle :: SingPingPong Idle
  SingBusy :: SingPingPong Busy
  SingDone :: SingPingPong Done

instance Typeable st => StateTokenI (st :: PingPong) where
  stateToken =     case eqT @st @StIdle of
    { Just Refl -> SingIdle
    ; Nothing   -> case eqT @st @StBusy of
    { Just Refl -> SingBusy
    ; Nothing   -> case eqT @st @StDone of
    { Just Refl -> SingDone
    ; Nothing   -> error "impossible"
    }}}

When using Cardano Haskell package repository, typed-protocols fails to build.

Describe the bug
We are using the Cardano Haskell package repository to bring in the cardano-api-1.35.3 dependency. We followed the instructions as described in Cardano Haskell package repository but when building the project we encounter the following build error:

Preprocessing library for typed-protocols-0.1.0.1..
Building library for typed-protocols-0.1.0.1..
[1 of 6] Compiling Network.TypedProtocol.Core ( src/Network/TypedProtocol/Core.hs, dist/build/Network/TypedProtocol/Core.o, dist/build/Network/TypedProtocol/Core.dyn_o )
[2 of 6] Compiling Network.TypedProtocol.Pipelined ( src/Network/TypedProtocol/Pipelined.hs, dist/build/Network/TypedProtocol/Pipelined.o, dist/build/Network/TypedProtocol/Pipelined.dyn_o )
[3 of 6] Compiling Network.TypedProtocol.Driver ( src/Network/TypedProtocol/Driver.hs, dist/build/Network/TypedProtocol/Driver.o, dist/build/Network/TypedProtocol/Driver.dyn_o )

src/Network/TypedProtocol/Driver.hs:29:1: error:
    Could not find module `Control.Concurrent.Class.MonadSTM.TQueue'
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
   |
29 | import           Control.Concurrent.Class.MonadSTM.TQueue
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cabal: Failed to build cardano-crypto-wrapper-1.3.0

Adding the io-classes repo as source-repository-package did not solve the issue.

To Reproduce
Create a your-project.cabal file with cardano-api >= 1.35.3 as dependency, and a cabal.project file which contains the cardano-haskell-packages repository with the current date as index-state.

Expected behaviour
Build should succeed.

Desktop (please complete the following information):

  • GHC version: 8.10.7

Experiment with a bearer which allows to avoid extra threads in a Driver

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.

Using IndexMonad to improve typed-protocols

Is your feature request related to a problem? Please describe.

Peer is essentially a McBride style indexed monad.
With a slight modification to Peer, we can use do syntax to describe Peer.

new Peer implement:

https://github.com/sdzx-1/typed-protocols-i/blob/685fb27ec0b57bab508874127c578ead22807a28/src/Network/TypedProtocol/Core.hs#L67

use do syntax to describe Peer:
https://github.com/sdzx-1/typed-protocols-i/blob/685fb27ec0b57bab508874127c578ead22807a28/test/Network/Core.hs#L46

pingPongClientPeer :: Has (State Int) sig m 
                   => Peer PingPong AsClient m (At () StDone) StIdle
pingPongClientPeer = I.do
    -- ghc bug: https://gitlab.haskell.org/ghc/ghc/-/issues/22788 
    -- we can't use this: 
    -- i <- effect (get @Int) 
    -- if i > 10 
    --   then  ...
    --   else ...
    --
    effect (get @Int) I.>>= 
      \(At i ) ->
         if i > 10 
          then I.do
            yield (ClientAgency TokIdle) MsgDone
            done TokDone ()
          else I.do
            yield (ClientAgency TokIdle) MsgPing 
            effect $ modify @Int (+1)
            await (ServerAgency TokBusy) I.>>= \case
              MsgPong -> pingPongClientPeer

pingPongServerPeer :: Has (Lift IO ) sig m 
                   => Peer PingPong AsServer m (At () StDone) StIdle
pingPongServerPeer = Indexed.do 
    await (ClientAgency TokIdle) I.>>= \case
      MsgDone -> I.do 
        effect $ sendIO (print "recv MsgDone, finish")
        done TokDone ()
      MsgPing -> I.do 
        effect $ sendIO (print "recv MsgPing, send MsgPong")
        yield (ServerAgency TokBusy) MsgPong
        pingPongServerPeer

Describe the solution you'd like

Describe alternatives you've considered

Additional context

ghc currently has a bug in the QualifiedDo syntax, sometimes makes the code look weird.
https://gitlab.haskell.org/ghc/ghc/-/issues/22788

the code I expect:

pingPongClientPeer' :: Has (State Int) sig m 
                   => Peer PingPong AsClient m (At () StDone) StIdle
pingPongClientPeer' = I.do
  At i <- effect (get @Int) 
  if i > 10 
   then I.do
     yield (ClientAgency TokIdle) MsgDone
     done TokDone ()
   else I.do
     yield (ClientAgency TokIdle) MsgPing 
     effect $ modify @Int (+1)
     await (ServerAgency TokBusy) I.>>= \case
       MsgPong -> pingPongClientPeer

Are you willing to implement it?

  • Are you? yes

@coot

Add bounds where appropriate

At least, it would be useful to have bound on io-classes and maybe some others, and also between the packages in this repo (since in practice they likely only build with the "same" version of the other packages).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.