├── .github └── workflows │ └── haskell.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── cabal.project ├── data ├── example.gif └── fm.png ├── desigen.dio ├── examples ├── Bank │ └── Bank.hs ├── Book3 │ ├── Buyer.hs │ ├── Buyer2.hs │ └── Seller.hs ├── KV │ └── Main.hs ├── PingPong │ ├── Client.hs │ ├── Counter.hs │ └── Server.hs ├── Ring │ └── Main.hs ├── cabal.project ├── examples.cabal └── src │ ├── Bank │ ├── Peer.hs │ ├── Protocol.hs │ └── Type.hs │ ├── Book3 │ ├── Codec.hs │ ├── Peer.hs │ ├── Protocol.hs │ └── Type.hs │ ├── KV │ ├── Peer.hs │ ├── Protocol.hs │ └── Type.hs │ ├── PingPong │ ├── Peer.hs │ ├── Protocol.hs │ └── Type.hs │ └── Ring │ ├── Peer.hs │ ├── Protocol.hs │ └── Type.hs ├── src ├── Data │ └── IFunctor.hs └── TypedSession │ ├── Codec.hs │ ├── Core.hs │ ├── Driver.hs │ └── TH.hs ├── test ├── Book3 │ ├── Main.hs │ ├── Peer.hs │ ├── Protocol.hs │ └── Type.hs └── Main.hs └── typed-session.cabal /.github/workflows/haskell.yml: -------------------------------------------------------------------------------- 1 | name: Haskell CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: haskell-actions/setup@v2.7.2 20 | with: 21 | ghc-version: '9.10.1' 22 | cabal-version: '3.12.1.0' 23 | 24 | - name: Cache 25 | uses: actions/cache@v3 26 | env: 27 | cache-name: cache-cabal 28 | with: 29 | path: ~/.cabal 30 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} 31 | restore-keys: | 32 | ${{ runner.os }}-build-${{ env.cache-name }}- 33 | ${{ runner.os }}-build- 34 | ${{ runner.os }}- 35 | 36 | - name: Install dependencies 37 | run: | 38 | cabal update 39 | cabal build --only-dependencies --enable-tests --enable-benchmarks 40 | - name: Build 41 | run: cabal build --enable-tests --enable-benchmarks all 42 | - name: Run tests 43 | run: cabal test all 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.hie 7 | *.chi 8 | *.chs.h 9 | *.dyn_o 10 | *.dyn_hi 11 | .hpc 12 | .hsenv 13 | .cabal-sandbox/ 14 | cabal.sandbox.config 15 | *.prof 16 | *.aux 17 | *.hp 18 | *.eventlog 19 | .stack-work/ 20 | cabal.project.local 21 | cabal.project.local~ 22 | .HTF/ 23 | .ghc.environment.* 24 | *.prot -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Revision history for typed-session 2 | ## 0.2.0.0 3 | * Remove data Recv 4 | 5 | ## 0.1.1.0 6 | * add protocol TH 7 | 8 | ## 0.1.0.0-- 2024-8-6 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Yang Miao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typed-session 2 | 3 | ![img](data/example.gif) 4 | 5 | 6 | Typed session are used to ensure desirable properties in concurrent and distributed systems, i.e. absence of communication errors or deadlocks, and protocol conformance. 7 | 8 | [Introduction to typed-session](https://discourse.haskell.org/t/introduction-to-typed-session/10100) 9 | 10 | [How to use typed session](https://github.com/sdzx-1/How-to-use-typed-session) 11 | 12 | [typed-session-tutorial](https://github.com/sdzx-1/typed-session-tutorial) 13 | 14 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | 3 | tests: True 4 | -------------------------------------------------------------------------------- /data/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdzx-1/typed-session/1bccfea71a68094d4c7a2fb01f88f4f5770ceffa/data/example.gif -------------------------------------------------------------------------------- /data/fm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdzx-1/typed-session/1bccfea71a68094d4c7a2fb01f88f4f5770ceffa/data/fm.png -------------------------------------------------------------------------------- /desigen.dio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /examples/Bank/Bank.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Bank.Peer 4 | 5 | main = Bank.Peer.main 6 | -------------------------------------------------------------------------------- /examples/Book3/Buyer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | {-# OPTIONS_GHC -Wall #-} 3 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 4 | 5 | -- Echo server program 6 | module Main (main) where 7 | 8 | import Book3.Codec 9 | import Book3.Peer 10 | import Book3.Protocol 11 | import Control.Carrier.Random.Gen (runRandom) 12 | import qualified Control.Exception as E 13 | import Control.Monad (void) 14 | import Control.Monad.Class.MonadFork 15 | import Control.Monad.IO.Class (liftIO) 16 | import Network.Socket 17 | import System.Random (newStdGen) 18 | import TypedSession.Codec (Decode (..)) 19 | import TypedSession.Driver 20 | import Control.Carrier.Lift (runM) 21 | 22 | main :: IO () 23 | main = runTCPServer Nothing "3000" 24 | 25 | runTCPServer :: Maybe HostName -> ServiceName -> IO () 26 | runTCPServer mhost port = withSocketsDo $ do 27 | addr <- resolve 28 | E.bracket (open addr) close start 29 | where 30 | resolve = do 31 | let hints = 32 | defaultHints 33 | { addrFlags = [AI_PASSIVE] 34 | , addrSocketType = Stream 35 | } 36 | head <$> getAddrInfo (Just hints) mhost (Just port) 37 | 38 | open addr = E.bracketOnError (openSocket addr) close $ \sock -> do 39 | setSocketOption sock ReuseAddr 1 40 | withFdSocket sock setCloseOnExecIfNeeded 41 | bind sock $ addrAddress addr 42 | listen sock 1024 43 | return sock 44 | 45 | start sock = do 46 | (buyer2, _peer) <- accept sock 47 | (seller, _peer) <- accept sock 48 | 49 | let buyer2Channel = socketAsChannel buyer2 50 | sellerChannel = socketAsChannel seller 51 | 52 | buyerDriver <- 53 | driverSimple 54 | (myTracer "buyer :") 55 | encodeMsg 56 | (Decode decodeMsg) 57 | [ (SomeRole SBuyer2, buyer2Channel) 58 | , (SomeRole SSeller, sellerChannel) 59 | ] 60 | liftIO 61 | 62 | g <- newStdGen 63 | 64 | void $ runM $ runRandom g $ runPeerWithDriver buyerDriver buyerPeer 65 | 66 | close buyer2 67 | close seller 68 | -------------------------------------------------------------------------------- /examples/Book3/Buyer2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | -- Echo client program 4 | module Main (main) where 5 | 6 | import Book3.Codec (decodeMsg, encodeMsg, myTracer, socketAsChannel) 7 | import Book3.Peer 8 | import Book3.Protocol 9 | import Book3.Type 10 | import Control.Carrier.Random.Gen (runRandom) 11 | import Control.Concurrent.Class.MonadSTM 12 | import qualified Control.Exception as E 13 | import Control.Monad.Class.MonadFork 14 | import Control.Monad.IO.Class (liftIO) 15 | import qualified Data.ByteString.Char8 as C 16 | import Data.Functor (void) 17 | import qualified Data.IntMap as IntMap 18 | import Network.Socket 19 | import Network.Socket.ByteString (recv, sendAll) 20 | import System.Random (newStdGen) 21 | import TypedSession.Codec (Decode (..)) 22 | import qualified TypedSession.Codec as C 23 | import TypedSession.Core (SingToInt (singToInt)) 24 | import TypedSession.Driver 25 | import TypedSession.Driver (driverSimple) 26 | import Control.Carrier.Lift (runM) 27 | 28 | main :: IO () 29 | main = runTCPClient "127.0.0.1" "3000" 30 | 31 | runTCPClient :: HostName -> ServiceName -> IO () 32 | runTCPClient host port = withSocketsDo $ do 33 | addr <- resolve 34 | E.bracket (open addr) close client 35 | where 36 | resolve = do 37 | let hints = defaultHints{addrSocketType = Stream} 38 | head <$> getAddrInfo (Just hints) (Just host) (Just port) 39 | 40 | open addr = E.bracketOnError (openSocket addr) close $ \sock -> do 41 | connect sock $ addrAddress addr 42 | return sock 43 | 44 | client sock = do 45 | buyer2Driver <- driverSimple (myTracer "buyer2 :") encodeMsg (Decode decodeMsg) [(SomeRole SBuyer, socketAsChannel sock)] liftIO 46 | g <- newStdGen 47 | void $ runM $ runRandom g $ runPeerWithDriver buyer2Driver buyer2Peer 48 | -------------------------------------------------------------------------------- /examples/Book3/Seller.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE TypeApplications #-} 3 | 4 | -- Echo client program 5 | module Main (main) where 6 | 7 | import Book3.Codec (decodeMsg, encodeMsg, myTracer, socketAsChannel) 8 | import Book3.Peer 9 | import Book3.Protocol 10 | import Book3.Type 11 | import Control.Carrier.Random.Gen (runRandom) 12 | import Control.Carrier.State.Strict (runState) 13 | import Control.Concurrent.Class.MonadSTM 14 | import qualified Control.Exception as E 15 | import Control.Monad.Class.MonadFork 16 | import Control.Monad.IO.Class (liftIO) 17 | import qualified Data.ByteString.Char8 as C 18 | import Data.Functor (void) 19 | import qualified Data.IntMap as IntMap 20 | import Network.Socket 21 | import Network.Socket.ByteString (recv, sendAll) 22 | import System.Random (newStdGen) 23 | import TypedSession.Codec (Decode (..)) 24 | import qualified TypedSession.Codec as C 25 | import TypedSession.Core (SingToInt (singToInt)) 26 | import TypedSession.Driver 27 | import TypedSession.Driver (driverSimple) 28 | import Control.Carrier.Lift (runM) 29 | 30 | main :: IO () 31 | main = runTCPClient "127.0.0.1" "3000" 32 | 33 | runTCPClient :: HostName -> ServiceName -> IO () 34 | runTCPClient host port = withSocketsDo $ do 35 | addr <- resolve 36 | E.bracket (open addr) close client 37 | where 38 | resolve = do 39 | let hints = defaultHints{addrSocketType = Stream} 40 | head <$> getAddrInfo (Just hints) (Just host) (Just port) 41 | 42 | open addr = E.bracketOnError (openSocket addr) close $ \sock -> do 43 | connect sock $ addrAddress addr 44 | return sock 45 | 46 | client sock = do 47 | sellerDriver <- driverSimple (myTracer "seller :") encodeMsg (Decode decodeMsg) [(SomeRole SBuyer, socketAsChannel sock)] liftIO 48 | g <- newStdGen 49 | void $ runM $ runRandom g $ runState @Int 0 $ runPeerWithDriver sellerDriver sellerPeer 50 | -------------------------------------------------------------------------------- /examples/KV/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import KV.Peer 4 | 5 | main :: IO () 6 | main = KV.Peer.main -------------------------------------------------------------------------------- /examples/PingPong/Client.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | -- Echo client program 4 | module Main (main) where 5 | 6 | import Control.Concurrent.Class.MonadSTM 7 | import qualified Control.Exception as E 8 | import Control.Monad (void) 9 | import Control.Monad.Class.MonadFork 10 | import qualified Data.IntMap as IntMap 11 | import Network.Socket 12 | import PingPong.Peer 13 | import PingPong.Protocol 14 | import PingPong.Type 15 | import TypedSession.Codec (Decode (..)) 16 | import qualified TypedSession.Codec as C 17 | import TypedSession.Core 18 | import TypedSession.Driver (SomeRole (SomeRole), decodeLoop, driverSimple, runPeerWithDriver) 19 | 20 | main :: IO () 21 | main = runTCPClient 22 | 23 | getSocket :: HostName -> ServiceName -> IO Socket 24 | getSocket host port = do 25 | addr <- resolve 26 | open addr 27 | where 28 | resolve = do 29 | let hints = defaultHints{addrSocketType = Stream} 30 | head <$> getAddrInfo (Just hints) (Just host) (Just port) 31 | 32 | open addr = E.bracketOnError (openSocket addr) close $ \sock -> do 33 | connect sock $ addrAddress addr 34 | return sock 35 | 36 | runTCPClient :: IO () 37 | runTCPClient = withSocketsDo $ do 38 | E.bracket 39 | ( do 40 | serverSock <- getSocket "127.0.0.1" "3000" 41 | countSock <- getSocket "127.0.0.1" "3001" 42 | pure (serverSock, countSock) 43 | ) 44 | (\(a, b) -> close a >> close b) 45 | (client) 46 | where 47 | client (serverSock, countSock) = do 48 | let serverChannel = socketAsChannel serverSock 49 | counterChannel = socketAsChannel countSock 50 | clientDriver <- 51 | driverSimple 52 | (myTracer "client: ") 53 | encodeMsg 54 | (Decode decodeMsg) 55 | [ (SomeRole SServer, serverChannel) 56 | , (SomeRole SCounter, counterChannel) 57 | ] 58 | id 59 | 60 | void $ runPeerWithDriver clientDriver (clientPeer 0) 61 | -------------------------------------------------------------------------------- /examples/PingPong/Counter.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import qualified Control.Exception as E 4 | import qualified Data.IntMap as IntMap 5 | import Network.Socket 6 | 7 | import Control.Concurrent.Class.MonadSTM 8 | import Control.Monad (void) 9 | import Control.Monad.Class.MonadFork 10 | import PingPong.Peer 11 | import PingPong.Protocol 12 | import PingPong.Type 13 | import TypedSession.Codec (Decode (..)) 14 | import qualified TypedSession.Codec as C 15 | import TypedSession.Core (singToInt) 16 | import TypedSession.Driver (SomeRole (SomeRole), decodeLoop, driverSimple, runPeerWithDriver) 17 | 18 | main :: IO () 19 | main = runTCPServer Nothing "3001" 20 | 21 | runTCPServer :: Maybe HostName -> ServiceName -> IO () 22 | runTCPServer mhost port = withSocketsDo $ do 23 | addr <- resolve 24 | E.bracket (open addr) close start 25 | where 26 | resolve = do 27 | let hints = 28 | defaultHints 29 | { addrFlags = [AI_PASSIVE] 30 | , addrSocketType = Stream 31 | } 32 | head <$> getAddrInfo (Just hints) mhost (Just port) 33 | 34 | open addr = E.bracketOnError (openSocket addr) close $ \sock -> do 35 | setSocketOption sock ReuseAddr 1 36 | withFdSocket sock setCloseOnExecIfNeeded 37 | bind sock $ addrAddress addr 38 | listen sock 1024 39 | return sock 40 | 41 | start sock = do 42 | (client, _peer) <- accept sock 43 | 44 | let clientChannel = socketAsChannel client 45 | 46 | counterDriver <- driverSimple (myTracer "counter: ") encodeMsg (Decode decodeMsg) [(SomeRole SClient, clientChannel)] id 47 | 48 | void $ runPeerWithDriver counterDriver (counterPeer 0) 49 | 50 | close client 51 | -------------------------------------------------------------------------------- /examples/PingPong/Server.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Control.Concurrent.Class.MonadSTM 4 | import qualified Control.Exception as E 5 | import Control.Monad (void) 6 | import Control.Monad.Class.MonadFork 7 | import qualified Data.IntMap as IntMap 8 | import Network.Socket 9 | import PingPong.Peer 10 | import PingPong.Protocol 11 | import PingPong.Type 12 | import TypedSession.Codec (Decode (..)) 13 | import qualified TypedSession.Codec as C 14 | import TypedSession.Core (singToInt) 15 | import TypedSession.Driver (SomeRole (SomeRole), decodeLoop, driverSimple, runPeerWithDriver) 16 | 17 | main :: IO () 18 | main = runTCPServer Nothing "3000" 19 | 20 | runTCPServer :: Maybe HostName -> ServiceName -> IO () 21 | runTCPServer mhost port = withSocketsDo $ do 22 | addr <- resolve 23 | E.bracket (open addr) close start 24 | where 25 | resolve = do 26 | let hints = 27 | defaultHints 28 | { addrFlags = [AI_PASSIVE] 29 | , addrSocketType = Stream 30 | } 31 | head <$> getAddrInfo (Just hints) mhost (Just port) 32 | 33 | open addr = E.bracketOnError (openSocket addr) close $ \sock -> do 34 | setSocketOption sock ReuseAddr 1 35 | withFdSocket sock setCloseOnExecIfNeeded 36 | bind sock $ addrAddress addr 37 | listen sock 1024 38 | return sock 39 | 40 | start sock = do 41 | (client, _peer) <- accept sock 42 | let clientChannel = socketAsChannel client 43 | serverDriver <- driverSimple (myTracer "server: ") encodeMsg (Decode decodeMsg) [(SomeRole SClient, clientChannel)] id 44 | void $ runPeerWithDriver serverDriver serverPeer 45 | close client 46 | -------------------------------------------------------------------------------- /examples/Ring/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Ring.Peer 4 | 5 | main :: IO () 6 | main = Ring.Peer.main -------------------------------------------------------------------------------- /examples/cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | , ../typed-session.cabal 3 | -------------------------------------------------------------------------------- /examples/examples.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | -- The cabal-version field refers to the version of the .cabal specification, 3 | -- and can be different from the cabal-install (the tool) version and the 4 | -- Cabal (the library) version you are using. As such, the Cabal (the library) 5 | -- version used must be equal or greater than the version stated in this field. 6 | -- Starting from the specification version 2.2, the cabal-version field must be 7 | -- the first thing in the cabal file. 8 | 9 | -- Initial package description 'examples' generated by 10 | -- 'cabal init'. For further documentation, see: 11 | -- http://haskell.org/cabal/users-guide/ 12 | -- 13 | -- The name of the package. 14 | name: examples 15 | 16 | -- The package version. 17 | -- See the Haskell package versioning policy (PVP) for standards 18 | -- guiding when and how versions should be incremented. 19 | -- https://pvp.haskell.org 20 | -- PVP summary: +-+------- breaking API changes 21 | -- | | +----- non-breaking API additions 22 | -- | | | +--- code changes with no API change 23 | version: 0.1.0.0 24 | 25 | -- A short (one-line) description of the package. 26 | -- synopsis: 27 | 28 | -- A longer description of the package. 29 | -- description: 30 | 31 | -- The package author(s). 32 | author: sdzx-1 33 | 34 | -- An email address to which users can send suggestions, bug reports, and patches. 35 | maintainer: shangdizhixia1993@163.com 36 | 37 | -- A copyright notice. 38 | -- copyright: 39 | build-type: Simple 40 | 41 | -- Extra doc files to be distributed with the package, such as a CHANGELOG or a README. 42 | extra-doc-files: CHANGELOG.md 43 | 44 | -- Extra source files to be distributed with the package, such as examples, or a tutorial module. 45 | -- extra-source-files: 46 | 47 | 48 | library 49 | build-depends: base 50 | , typed-session 51 | , containers 52 | , io-classes 53 | , io-sim 54 | , random 55 | , fused-effects 56 | , fused-effects-random 57 | , binary 58 | , bytestring 59 | , network 60 | , template-haskell 61 | , split 62 | default-language: Haskell2010 63 | hs-source-dirs: src 64 | exposed-modules: Book3.Type 65 | , Book3.Peer 66 | , Book3.Codec 67 | , Book3.Protocol 68 | , PingPong.Type 69 | , PingPong.Protocol 70 | , PingPong.Peer 71 | , Bank.Type 72 | , Bank.Protocol 73 | , Bank.Peer 74 | , Ring.Peer 75 | , Ring.Protocol 76 | , Ring.Type 77 | , KV.Peer 78 | , KV.Protocol 79 | , KV.Type 80 | 81 | executable kv 82 | build-depends: base 83 | , examples 84 | main-is: Main.hs 85 | default-language: Haskell2010 86 | hs-source-dirs: KV 87 | 88 | executable ring 89 | build-depends: base 90 | , examples 91 | main-is: Main.hs 92 | default-language: Haskell2010 93 | hs-source-dirs: Ring 94 | 95 | 96 | executable bank 97 | build-depends: base 98 | , examples 99 | main-is: Bank.hs 100 | default-language: Haskell2010 101 | hs-source-dirs: Bank 102 | 103 | 104 | executable buyer 105 | build-depends: base 106 | , fused-effects 107 | , typed-session 108 | , io-classes 109 | , io-sim 110 | , random 111 | , containers 112 | , fused-effects-random 113 | , network 114 | , examples 115 | main-is: Buyer.hs 116 | default-language: Haskell2010 117 | hs-source-dirs: Book3 118 | 119 | 120 | executable seller 121 | build-depends: base 122 | , fused-effects 123 | , typed-session 124 | , io-classes 125 | , io-sim 126 | , random 127 | , containers 128 | , fused-effects-random 129 | , network 130 | , examples 131 | , bytestring 132 | main-is: Seller.hs 133 | default-language: Haskell2010 134 | hs-source-dirs: Book3 135 | 136 | executable buyer2 137 | build-depends: base 138 | , fused-effects 139 | , typed-session 140 | , io-classes 141 | , io-sim 142 | , random 143 | , containers 144 | , fused-effects-random 145 | , network 146 | , examples 147 | , bytestring 148 | main-is: Buyer2.hs 149 | default-language: Haskell2010 150 | hs-source-dirs: Book3 151 | 152 | 153 | executable server 154 | build-depends: base 155 | , typed-session 156 | , containers 157 | , io-classes 158 | , io-sim 159 | , random 160 | , fused-effects 161 | , fused-effects-random 162 | , binary 163 | , bytestring 164 | , network 165 | , examples 166 | main-is: Server.hs 167 | default-language: Haskell2010 168 | hs-source-dirs: PingPong 169 | 170 | executable client 171 | build-depends: base 172 | , typed-session 173 | , containers 174 | , io-classes 175 | , io-sim 176 | , random 177 | , fused-effects 178 | , fused-effects-random 179 | , binary 180 | , bytestring 181 | , network 182 | , examples 183 | main-is: Client.hs 184 | default-language: Haskell2010 185 | hs-source-dirs: PingPong 186 | 187 | 188 | executable counter 189 | build-depends: base 190 | , typed-session 191 | , containers 192 | , io-classes 193 | , io-sim 194 | , random 195 | , fused-effects 196 | , fused-effects-random 197 | , binary 198 | , bytestring 199 | , network 200 | , examples 201 | main-is: Counter.hs 202 | default-language: Haskell2010 203 | hs-source-dirs: PingPong -------------------------------------------------------------------------------- /examples/src/Bank/Peer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-# LANGUAGE LambdaCase #-} 4 | {-# LANGUAGE QualifiedDo #-} 5 | {-# LANGUAGE TypeApplications #-} 6 | {-# OPTIONS_GHC -Wall #-} 7 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 8 | 9 | module Bank.Peer where 10 | 11 | import Bank.Protocol 12 | import Bank.Type 13 | 14 | import Control.Concurrent.Class.MonadSTM 15 | import Control.Monad 16 | import Control.Monad.Class.MonadFork (forkIO) 17 | import Data.IFunctor (At (..), returnAt) 18 | import qualified Data.IFunctor as I 19 | import qualified Data.IntMap as IntMap 20 | import TypedSession.Core 21 | import TypedSession.Driver 22 | 23 | choice :: ChoiceNextActionFun IO 24 | choice = I.do 25 | At tr <- liftm $ do 26 | putStrLn "Input command:" 27 | getLine 28 | case tr of 29 | "q" -> liftConstructor BranchSt_Finish 30 | st -> liftConstructor (BranchSt_Continue (parse st)) 31 | 32 | clientPeer 33 | :: Peer BankRole Bank Client IO (At () Done) ClientStartSt 34 | clientPeer = do 35 | choice I.>>= \case 36 | BranchSt_Finish -> I.do 37 | yield CStop 38 | BranchSt_Continue tr -> I.do 39 | yield (Command tr) 40 | await I.>>= \case 41 | ValidFailed -> I.do 42 | liftm $ putStrLn "valid failed" 43 | clientPeer 44 | ValidSuccessed -> I.do 45 | liftm $ putStrLn "valid successed" 46 | clientPeer 47 | 48 | validC :: Bool -> Bool -> ValidResultFun IO 49 | validC ar br = 50 | if ar && br 51 | then liftConstructor BranchSt_CTrue 52 | else liftConstructor BranchSt_CFalse 53 | 54 | coordinatorPeer 55 | :: Peer BankRole Bank Coordinator IO (At () Done) (CoordinatorStartSt s) 56 | coordinatorPeer = do 57 | await I.>>= \case 58 | CStop -> I.do 59 | yield BStop 60 | yield AStop 61 | Command tr -> I.do 62 | yield (Transaction1 tr) 63 | yield (Transaction2 tr) 64 | AliceValidResult ar <- await 65 | BobValidResult br <- await 66 | validC ar br I.>>= \case 67 | BranchSt_CFalse -> I.do 68 | yield ValidFailed 69 | yield ValidFailedA 70 | yield ValidFailedB 71 | coordinatorPeer 72 | BranchSt_CTrue -> I.do 73 | yield ValidSuccessed 74 | yield ValidSuccessedA 75 | yield ValidSuccessedB 76 | coordinatorPeer 77 | 78 | alicePeer :: Int -> Peer BankRole Bank Alice IO (At () Done) (AliceStartSt s) 79 | alicePeer val = I.do 80 | liftm $ putStrLn $ "alice's val: " ++ show val 81 | await I.>>= \case 82 | Transaction1 tr -> I.do 83 | let (valid, val') = validate "alice" val tr 84 | yield (AliceValidResult valid) 85 | await I.>>= \case 86 | ValidFailedA -> alicePeer val 87 | ValidSuccessedA -> alicePeer val' 88 | AStop -> returnAt () 89 | 90 | bobPeer :: Int -> Peer BankRole Bank Bob IO (At () Done) (BobStartSt s) 91 | bobPeer val = I.do 92 | liftm $ putStrLn $ "Bob's val: " ++ show val 93 | await I.>>= \case 94 | Transaction2 tr -> I.do 95 | let (valid, val') = validate "bob" val tr 96 | yield (BobValidResult valid) 97 | await I.>>= \case 98 | ValidFailedB -> bobPeer val 99 | ValidSuccessedB -> bobPeer val' 100 | BStop -> returnAt () 101 | 102 | main :: IO () 103 | main = do 104 | let rg = [Client .. Bob] 105 | vs <- forM rg $ \r -> do 106 | tvar <- newTVarIO IntMap.empty 107 | pure (fromEnum r, tvar) 108 | let allMap = IntMap.fromList vs 109 | driver r = localDriverSimple (\v -> putStrLn (show v)) allMap r id 110 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SCoordinator) coordinatorPeer 111 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SAlice) (alicePeer 0) 112 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SBob) (bobPeer 0) 113 | void $ runPeerWithDriver (driver $ SomeRole SClient) clientPeer -------------------------------------------------------------------------------- /examples/src/Bank/Protocol.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE ExistentialQuantification #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE KindSignatures #-} 6 | {-# LANGUAGE MagicHash #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE QuasiQuotes #-} 9 | {-# LANGUAGE TemplateHaskell #-} 10 | {-# LANGUAGE TypeFamilies #-} 11 | {-# OPTIONS_GHC -Wall #-} 12 | {-# OPTIONS_GHC -Wno-orphans #-} 13 | {-# OPTIONS_GHC -ddump-splices #-} 14 | 15 | module Bank.Protocol where 16 | 17 | import Bank.Type 18 | import TypedSession.Core 19 | 20 | [bankProtocol| 21 | Label 0 22 | Branch Client ChoiceNextAction { 23 | BranchSt Finish [] 24 | Msg CStop [] Client Coordinator 25 | Msg BStop [] Coordinator Bob 26 | Msg AStop [] Coordinator Alice 27 | Terminal 28 | BranchSt Continue [Transaction] 29 | Msg Command [Transaction] Client Coordinator 30 | Msg Transaction1 [Transaction] Coordinator Alice 31 | Msg Transaction2 [Transaction] Coordinator Bob 32 | Msg AliceValidResult [Bool] Alice Coordinator 33 | Msg BobValidResult [Bool] Bob Coordinator 34 | Branch Coordinator ValidResult { 35 | BranchSt CFalse [] 36 | Msg ValidFailed [] Coordinator Client 37 | Msg ValidFailedA [] Coordinator Alice 38 | Msg ValidFailedB [] Coordinator Bob 39 | Goto 0 40 | BranchSt CTrue [] 41 | Msg ValidSuccessed [] Coordinator Client 42 | Msg ValidSuccessedA [] Coordinator Alice 43 | Msg ValidSuccessedB [] Coordinator Bob 44 | Goto 0 45 | 46 | } 47 | } 48 | |] 49 | 50 | instance Show (AnyMsg BankRole Bank) where 51 | show (AnyMsg msg) = case msg of 52 | CStop -> "CStop" 53 | BStop -> "BStop" 54 | AStop -> "AStop" 55 | Command tr -> "Command " ++ show tr 56 | Transaction1 tr -> "Transaction1 " ++ show tr 57 | Transaction2 tr -> "Transaction2 " ++ show tr 58 | AliceValidResult b -> "AliceValidResult " ++ show b 59 | BobValidResult b -> "BobValidResult " ++ show b 60 | ValidFailed -> "ValidFailed" 61 | ValidFailedA -> "ValidFailedA" 62 | ValidFailedB -> "ValidFailedB" 63 | ValidSuccessed -> "ValidSuccessed" 64 | ValidSuccessedA -> "ValidSuccessedA" 65 | ValidSuccessedB -> "ValidSuccessedB" 66 | -------------------------------------------------------------------------------- /examples/src/Bank/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE NamedFieldPuns #-} 10 | {-# LANGUAGE NumericUnderscores #-} 11 | {-# LANGUAGE PolyKinds #-} 12 | {-# LANGUAGE QualifiedDo #-} 13 | {-# LANGUAGE RankNTypes #-} 14 | {-# LANGUAGE ScopedTypeVariables #-} 15 | {-# LANGUAGE TemplateHaskell #-} 16 | {-# LANGUAGE TypeApplications #-} 17 | {-# LANGUAGE TypeFamilies #-} 18 | {-# OPTIONS_GHC -Wall #-} 19 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 20 | 21 | module Bank.Type where 22 | 23 | import Language.Haskell.TH.Quote 24 | import TypedSession.TH 25 | import Data.List.Split (splitOn) 26 | import Data.Maybe (mapMaybe) 27 | import Text.Read (readMaybe) 28 | 29 | {- 30 | bank-2pc 31 | 32 | https://github.com/gshen42/HasChor/tree/main/examples/bank-2pc 33 | -} 34 | 35 | data BankRole = Client | Coordinator | Alice | Bob 36 | deriving (Show, Eq, Ord, Enum, Bounded) 37 | 38 | data BankBranchSt = Continue | Finish | CTrue | CFalse 39 | deriving (Show, Read, Eq, Ord, Enum, Bounded) 40 | 41 | bankProtocol :: QuasiQuoter 42 | bankProtocol = protocol @BankRole @BankBranchSt "Bank" ''BankRole ''BankBranchSt 43 | 44 | type Action = (String, Int) 45 | 46 | type Transaction = [Action] 47 | 48 | validate :: String -> Int -> Transaction -> (Bool, Int) 49 | validate name balance tx = 50 | foldl (\(valid, i) (_, amount) -> (let next = i + amount in (valid && next >= 0, next))) (True, balance) actions 51 | where 52 | actions = filter (\(n, _) -> n == name) tx 53 | 54 | parse :: String -> Transaction 55 | parse s = tx 56 | where 57 | t = splitOn ";" s 58 | f :: String -> Maybe Action 59 | f l = do 60 | [target, amountStr] <- return $ words l 61 | amount <- readMaybe amountStr :: Maybe Int 62 | target' <- if target == "alice" || target == "bob" then Just target else Nothing 63 | return (target', amount) 64 | tx = mapMaybe f t -------------------------------------------------------------------------------- /examples/src/Book3/Codec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE NamedFieldPuns #-} 10 | {-# LANGUAGE PolyKinds #-} 11 | {-# LANGUAGE QualifiedDo #-} 12 | {-# LANGUAGE RankNTypes #-} 13 | {-# LANGUAGE ScopedTypeVariables #-} 14 | {-# LANGUAGE TypeFamilies #-} 15 | {-# OPTIONS_GHC -Wall #-} 16 | 17 | module Book3.Codec where 18 | 19 | import Book3.Protocol 20 | import Book3.Type 21 | import Control.Monad.Class.MonadSay (MonadSay (say)) 22 | import Data.Binary 23 | import Data.Binary.Get 24 | import Data.Binary.Put 25 | import qualified Data.ByteString as BS 26 | import qualified Data.ByteString.Builder.Extra as L 27 | import qualified Data.ByteString.Lazy as L 28 | import qualified Network.Socket as Socket 29 | import qualified Network.Socket.ByteString as Socket 30 | import TypedSession.Codec 31 | import TypedSession.Core 32 | import TypedSession.Driver 33 | 34 | encodeMsg :: Encode BookRole Book L.ByteString 35 | encodeMsg = Encode $ \x -> runPut $ case x of 36 | Title st -> putWord8 0 >> put st 37 | NoBook -> putWord8 1 38 | SellerNoBook -> putWord8 2 39 | Price i -> putWord8 3 >> put i 40 | OneAccept -> putWord8 5 41 | OneDate i -> putWord8 6 >> put i 42 | OneSuccess i -> putWord8 7 >> put i 43 | PriceToBuyer2 i -> putWord8 8 >> put i 44 | NotSupport1 -> putWord8 9 45 | TwoNotBuy -> putWord8 10 46 | SupportVal i -> putWord8 11 >> put i 47 | TwoAccept -> putWord8 12 48 | TwoDate i -> putWord8 13 >> put i 49 | TwoSuccess i -> putWord8 14 >> put i 50 | TwoNotBuy1 -> putWord8 15 51 | TwoFailed -> putWord8 16 52 | FinishBuyer -> putWord8 17 53 | FinishBuyer2 -> putWord8 18 54 | 55 | getAnyMsg :: Get (AnyMsg BookRole Book) 56 | getAnyMsg = do 57 | v <- getWord8 58 | case v of 59 | -- Title st -> putWord8 0 >> put st 60 | 0 -> get >>= pure . AnyMsg . Title 61 | -- NoBook -> putWord8 1 62 | 1 -> pure (AnyMsg NoBook) 63 | -- SellerNoBook -> putWord8 2 64 | 2 -> pure (AnyMsg SellerNoBook) 65 | -- Price i -> putWord8 3 >> put i 66 | 3 -> get >>= pure . AnyMsg . Price 67 | -- OneAccept -> putWord8 5 68 | 5 -> pure (AnyMsg OneAccept) 69 | -- OneDate i -> putWord8 6 >> put i 70 | 6 -> get >>= pure . AnyMsg . OneDate 71 | -- OneSuccess i -> putWord8 7 >> put i 72 | 7 -> get >>= pure . AnyMsg . OneSuccess 73 | -- PriceToBuyer2 i -> putWord8 8 >> put i 74 | 8 -> get >>= pure . AnyMsg . PriceToBuyer2 75 | -- NotSupport1 -> putWord8 9 76 | 9 -> pure (AnyMsg NotSupport1) 77 | -- TwoNotBuy -> putWord8 10 78 | 10 -> pure (AnyMsg TwoNotBuy) 79 | -- SupportVal i -> putWord8 11 >> put i 80 | 11 -> get >>= pure . AnyMsg . SupportVal 81 | -- TwoAccept -> putWord8 12 82 | 12 -> pure (AnyMsg TwoAccept) 83 | -- TwoDate i -> putWord8 13 >> put i 84 | 13 -> get >>= pure . AnyMsg . TwoDate 85 | -- TwoSuccess i -> putWord8 14 >> put i 86 | 14 -> get >>= pure . AnyMsg . TwoSuccess 87 | -- TwoNotBuy1 -> putWord8 15 88 | 15 -> pure (AnyMsg TwoNotBuy1) 89 | -- TwoFailed -> putWord8 16 90 | 16 -> pure (AnyMsg TwoFailed) 91 | 17 -> pure (AnyMsg FinishBuyer) 92 | 18 -> pure (AnyMsg FinishBuyer2) 93 | i -> error $ "undefined index: " ++ show i 94 | 95 | convertDecoderLBS1 96 | :: Decoder a 97 | -> (DecodeStep L.ByteString CodecFailure a) 98 | convertDecoderLBS1 = go 99 | where 100 | go :: Decoder a -> DecodeStep L.ByteString CodecFailure a 101 | go (Done tr _ a) = DecodeDone a (Just $ L.fromStrict tr) 102 | go (Fail _ _ e) = DecodeFail (CodecFailure e) 103 | go (Partial k) = DecodePartial $ \mbs -> case mbs of 104 | Nothing -> DecodeFail (CodecFailure "Peer disconnected!!") 105 | Just _bs -> go (k $ fmap L.toStrict mbs) 106 | 107 | decodeMsg 108 | :: DecodeStep 109 | L.ByteString 110 | CodecFailure 111 | (AnyMsg BookRole Book) 112 | decodeMsg = convertDecoderLBS1 (runGetIncremental getAnyMsg) 113 | 114 | socketAsChannel :: Socket.Socket -> Channel IO L.ByteString 115 | socketAsChannel socket = 116 | Channel{send, recv} 117 | where 118 | send :: L.ByteString -> IO () 119 | send chunks = 120 | Socket.sendMany socket (L.toChunks chunks) 121 | 122 | recv :: IO (Maybe L.ByteString) 123 | recv = do 124 | chunk <- Socket.recv socket L.smallChunkSize 125 | if BS.null chunk 126 | then return Nothing 127 | else return (Just (L.fromStrict chunk)) 128 | 129 | myTracer :: (MonadSay m) => String -> Tracer BookRole Book m 130 | myTracer st v = say (st <> show v) -------------------------------------------------------------------------------- /examples/src/Book3/Peer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE NamedFieldPuns #-} 9 | {-# LANGUAGE PolyKinds #-} 10 | {-# LANGUAGE QualifiedDo #-} 11 | {-# LANGUAGE RankNTypes #-} 12 | {-# LANGUAGE ScopedTypeVariables #-} 13 | {-# LANGUAGE TypeApplications #-} 14 | {-# LANGUAGE TypeFamilies #-} 15 | {-# LANGUAGE TypeOperators #-} 16 | {-# OPTIONS_GHC -Wall #-} 17 | 18 | module Book3.Peer where 19 | 20 | import Book3.Protocol 21 | import Book3.Type 22 | import Control.Algebra ((:+:)) 23 | import Control.Effect.Random (Random, uniform) 24 | import Control.Effect.State 25 | import Data.IFunctor (At (..), returnAt) 26 | import qualified Data.IFunctor as I 27 | import TypedSession.Core 28 | 29 | budget :: Int 30 | budget = 16 31 | 32 | type Date = Int 33 | 34 | checkPrice 35 | :: (Has Random sig m) 36 | => Int 37 | -> Int 38 | -> EnoughtOrNotEnoughFun m 39 | checkPrice _i _h = I.do 40 | At b <- liftm $ uniform @Bool 41 | if b 42 | then liftConstructor BranchSt_Enough 43 | else liftConstructor BranchSt_NotEnough 44 | 45 | choiceOT 46 | :: (Has Random sig m) 47 | => Int 48 | -> OneOrTwoFun m 49 | choiceOT _i = I.do 50 | At b <- liftm $ uniform @Bool 51 | if b 52 | then liftConstructor BranchSt_One 53 | else liftConstructor BranchSt_Two 54 | 55 | buyerPeer 56 | :: (Has Random sig m) 57 | => Peer BookRole Book Buyer m (At (Maybe Date) Done) BuyerStartSt 58 | buyerPeer = I.do 59 | yield (Title "haskell book") 60 | await I.>>= \case 61 | FinishBuyer -> I.do 62 | yield FinishBuyer2 63 | returnAt Nothing 64 | NoBook -> I.do 65 | yield SellerNoBook 66 | buyerPeer 67 | (Price i) -> I.do 68 | choiceOT i I.>>= \case 69 | BranchSt_One -> I.do 70 | yield OneAccept 71 | (OneDate d) <- await 72 | yield (OneSuccess d) 73 | buyerPeer 74 | BranchSt_Two -> I.do 75 | yield (PriceToBuyer2 (i `div` 2)) 76 | await I.>>= \case 77 | NotSupport1 -> I.do 78 | yield TwoNotBuy 79 | buyerPeer 80 | (SupportVal h) -> I.do 81 | checkPrice 10 h I.>>= \case 82 | BranchSt_Enough -> I.do 83 | yield TwoAccept 84 | (TwoDate d) <- await 85 | yield (TwoSuccess d) 86 | buyerPeer 87 | BranchSt_NotEnough -> I.do 88 | yield TwoNotBuy1 89 | yield TwoFailed 90 | buyerPeer 91 | 92 | choiceB 93 | :: (Has Random sig m) 94 | => Int 95 | -> SupportOrNotSupportFun m 96 | choiceB _i = I.do 97 | At b <- liftm $ uniform @Bool 98 | if b 99 | then liftConstructor BranchSt_Support 100 | else liftConstructor BranchSt_NotSupport 101 | 102 | buyer2Peer 103 | :: (Has Random sig m) 104 | => Peer BookRole Book Buyer2 m (At (Maybe Date) Done) (Buyer2StartSt s) 105 | buyer2Peer = I.do 106 | await I.>>= \case 107 | FinishBuyer2 -> returnAt Nothing 108 | SellerNoBook -> buyer2Peer 109 | (OneSuccess d) -> buyer2Peer 110 | (PriceToBuyer2 i) -> I.do 111 | choiceB i I.>>= \case 112 | BranchSt_NotSupport -> I.do 113 | yield NotSupport1 114 | buyer2Peer 115 | BranchSt_Support -> I.do 116 | yield (SupportVal (i `div` 2)) 117 | await I.>>= \case 118 | (TwoSuccess d) -> buyer2Peer 119 | TwoFailed -> buyer2Peer 120 | 121 | findBook 122 | :: (Has (Random :+: State Int) sig m) 123 | => String 124 | -> ChoiceActionFun m 125 | findBook _st = I.do 126 | At i <- liftm $ get @Int 127 | if i > 30 128 | then liftConstructor BranchSt_Finish 129 | else I.do 130 | At b <- liftm $ uniform @Bool 131 | if b 132 | then liftConstructor BranchSt_Found 133 | else liftConstructor BranchSt_NotFound 134 | 135 | sellerPeer 136 | :: (Has (Random :+: State Int) sig m) 137 | => Peer BookRole Book Seller m (At () Done) SellerStartSt 138 | sellerPeer = I.do 139 | liftm $ modify @Int (+ 1) 140 | (Title st) <- await 141 | findBook st I.>>= \case 142 | BranchSt_Finish -> I.do 143 | yield FinishBuyer 144 | BranchSt_NotFound -> I.do 145 | yield NoBook 146 | sellerPeer 147 | BranchSt_Found -> I.do 148 | yield (Price 30) 149 | await I.>>= \case 150 | OneAccept -> I.do 151 | yield (OneDate 100) 152 | sellerPeer 153 | TwoNotBuy -> sellerPeer 154 | TwoAccept -> I.do 155 | yield (TwoDate 100) 156 | sellerPeer 157 | TwoNotBuy1 -> sellerPeer 158 | -------------------------------------------------------------------------------- /examples/src/Book3/Protocol.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | {-# LANGUAGE GADTs #-} 4 | {-# LANGUAGE KindSignatures #-} 5 | {-# LANGUAGE LambdaCase #-} 6 | {-# LANGUAGE MagicHash #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE PolyKinds #-} 9 | {-# LANGUAGE QuasiQuotes #-} 10 | {-# LANGUAGE TemplateHaskell #-} 11 | {-# LANGUAGE TypeApplications #-} 12 | {-# LANGUAGE TypeFamilies #-} 13 | {-# OPTIONS_GHC -Wno-orphans #-} 14 | {-# OPTIONS_GHC -ddump-splices #-} 15 | 16 | module Book3.Protocol where 17 | 18 | import Book3.Type 19 | 20 | import Data.IFunctor (Sing, SingI (sing)) 21 | import GHC.Exts (Int (..), dataToTag#) 22 | import TypedSession.Codec 23 | import TypedSession.Core 24 | 25 | [bookProtocol| 26 | Label 0 27 | Msg Title [String] Buyer Seller 28 | Branch Seller ChoiceAction{ 29 | BranchSt Finish [] 30 | Msg FinishBuyer [] Seller Buyer 31 | Msg FinishBuyer2 [] Buyer Buyer2 32 | Terminal 33 | BranchSt NotFound [] 34 | Msg NoBook [] Seller Buyer 35 | Msg SellerNoBook [] Buyer Buyer2 36 | Goto 0 37 | BranchSt Found [] 38 | Msg Price [Int] Seller Buyer 39 | Branch Buyer OneOrTwo { 40 | BranchSt One [] 41 | Msg OneAccept [] Buyer Seller 42 | Msg OneDate [Int] Seller Buyer 43 | Msg OneSuccess [Int] Buyer Buyer2 44 | Goto 0 45 | BranchSt Two [] 46 | Msg PriceToBuyer2 [Int] Buyer Buyer2 47 | Branch Buyer2 SupportOrNotSupport { 48 | BranchSt NotSupport [] 49 | Msg NotSupport1 [] Buyer2 Buyer 50 | Msg TwoNotBuy [] Buyer Seller 51 | Goto 0 52 | BranchSt Support [] 53 | Msg SupportVal [Int] Buyer2 Buyer 54 | Branch Buyer EnoughtOrNotEnough { 55 | BranchSt Enough [] 56 | Msg TwoAccept [] Buyer Seller 57 | Msg TwoDate [Int] Seller Buyer 58 | Msg TwoSuccess [Int] Buyer Buyer2 59 | Goto 0 60 | BranchSt NotEnough [] 61 | Msg TwoNotBuy1 [] Buyer Seller 62 | Msg TwoFailed [] Buyer Buyer2 63 | Goto 0 64 | } 65 | } 66 | } 67 | } 68 | 69 | |] 70 | 71 | instance Show (AnyMsg BookRole Book) where 72 | show (AnyMsg msg) = case msg of 73 | Title st -> "Title " <> show st 74 | NoBook -> "NoBook" 75 | SellerNoBook -> "SellerNoBook" 76 | Price i -> "Price " <> show i 77 | OneAccept -> "OneAccept" 78 | OneDate d -> "OneDate " <> show d 79 | OneSuccess d -> "OneSuccess" <> show d 80 | PriceToBuyer2 i -> "PriceToBuyer2 " <> show i 81 | NotSupport1 -> "NotSupport1" 82 | TwoNotBuy -> "TwoNotBuy" 83 | SupportVal v -> "SupportVal " <> show v 84 | TwoAccept -> "TwoAccept" 85 | TwoDate d -> "TwoDate " <> show d 86 | TwoSuccess d -> "TwoSuccess " <> show d 87 | TwoNotBuy1 -> "TwoNotBuy1" 88 | TwoFailed -> "TwoFailed" 89 | FinishBuyer -> "FinishBuyer" 90 | FinishBuyer2 -> "FinishBuyer2" -------------------------------------------------------------------------------- /examples/src/Book3/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE NamedFieldPuns #-} 10 | {-# LANGUAGE PolyKinds #-} 11 | {-# LANGUAGE QualifiedDo #-} 12 | {-# LANGUAGE RankNTypes #-} 13 | {-# LANGUAGE ScopedTypeVariables #-} 14 | {-# LANGUAGE TemplateHaskell #-} 15 | {-# LANGUAGE TypeApplications #-} 16 | {-# LANGUAGE TypeFamilies #-} 17 | 18 | module Book3.Type where 19 | 20 | import Data.IFunctor (Sing, SingI) 21 | import qualified Data.IFunctor as I 22 | import Data.Kind 23 | import GHC.Exts (dataToTag#) 24 | import GHC.Int (Int (I#)) 25 | import Language.Haskell.TH.Quote (QuasiQuoter) 26 | import TypedSession.Core 27 | import TypedSession.TH (protocol) 28 | 29 | data BookRole = Buyer | Seller | Buyer2 30 | deriving (Show, Eq, Ord, Enum, Bounded) 31 | 32 | data BookBranchSt 33 | = Finish 34 | | NotFound 35 | | Found 36 | | One 37 | | Two 38 | | Support 39 | | NotSupport 40 | | Enough 41 | | NotEnough 42 | deriving (Show, Eq, Ord, Enum, Bounded) 43 | 44 | bookProtocol :: QuasiQuoter 45 | bookProtocol = 46 | protocol 47 | @BookRole 48 | @BookBranchSt 49 | "Book" 50 | ''BookRole 51 | ''BookBranchSt 52 | -------------------------------------------------------------------------------- /examples/src/KV/Peer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-# LANGUAGE LambdaCase #-} 4 | {-# LANGUAGE QualifiedDo #-} 5 | {-# OPTIONS_GHC -Wall #-} 6 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 7 | 8 | module KV.Peer where 9 | 10 | import Control.Concurrent.Class.MonadSTM (MonadSTM (..)) 11 | import Control.Monad (void) 12 | import Control.Monad.Class.MonadFork (forkIO) 13 | import Data.IFunctor (At (At), returnAt) 14 | import qualified Data.IFunctor as I 15 | import Data.IORef 16 | import qualified Data.IntMap as IntMap 17 | import Data.Map (Map) 18 | import qualified Data.Map as Map 19 | import Data.Traversable (for) 20 | import KV.Protocol 21 | import KV.Type 22 | import TypedSession.Core 23 | import TypedSession.Driver (SomeRole (SomeRole), localDriverSimple, runPeerWithDriver) 24 | 25 | choice :: ChoiceNextActionFun IO 26 | choice = I.do 27 | At st <- liftm $ readRequest 28 | case st of 29 | RExit -> liftConstructor BranchSt_Stop 30 | RPut k v -> liftConstructor (BranchSt_Put k v) 31 | RGet k -> liftConstructor (BranchSt_Get k) 32 | 33 | clientPeer :: Peer KVRole KV Client IO (At () Done) ClientStartSt 34 | clientPeer = I.do 35 | choice I.>>= \case 36 | BranchSt_Stop -> I.do 37 | yield PStop 38 | yield BStop 39 | BranchSt_Put k v -> I.do 40 | yield (PutKV k v) 41 | clientPeer 42 | BranchSt_Get k -> I.do 43 | yield (GetKey k) 44 | GetKeyResult res <- await 45 | liftm $ putStrLn $ "Result: " <> show res 46 | clientPeer 47 | 48 | primaryPeer 49 | :: IORef (Map String String) 50 | -> Peer KVRole KV Primary IO (At () Done) (PrimaryStartSt s) 51 | primaryPeer kvmapRef = I.do 52 | await I.>>= \case 53 | PStop -> returnAt () 54 | PutKV k v -> I.do 55 | liftm $ modifyIORef' kvmapRef (Map.insert k v) 56 | yield (BackupKV k v) 57 | primaryPeer kvmapRef 58 | GetKey k -> I.do 59 | At v <- liftm $ (Map.lookup k) <$> readIORef kvmapRef 60 | yield (GetKeyResult v) 61 | primaryPeer kvmapRef 62 | 63 | backupPeer :: IORef (Map String String) -> Peer KVRole KV Backup IO (At () Done) (BackupStartSt s) 64 | backupPeer kvmapRef = I.do 65 | await I.>>= \case 66 | BStop -> returnAt () 67 | BackupKV k v -> I.do 68 | liftm $ do 69 | modifyIORef' kvmapRef (Map.insert k v) 70 | putStrLn $ "Backup: " <> k <> "," <> v 71 | backupPeer kvmapRef 72 | 73 | main :: IO () 74 | main = do 75 | let rg = [Client .. Backup] 76 | vs <- for rg $ \r -> do 77 | tvar <- newTVarIO IntMap.empty 78 | pure (fromEnum r, tvar) 79 | let allMap = IntMap.fromList vs 80 | driver someRole = localDriverSimple (\v -> putStrLn (show v)) allMap someRole id 81 | kvmapP <- newIORef Map.empty 82 | kvmapB <- newIORef Map.empty 83 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SPrimary) (primaryPeer kvmapP) 84 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SBackup) (backupPeer kvmapB) 85 | void $ runPeerWithDriver (driver $ SomeRole SClient) clientPeer -------------------------------------------------------------------------------- /examples/src/KV/Protocol.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE ExistentialQuantification #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE KindSignatures #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE QuasiQuotes #-} 10 | {-# LANGUAGE TemplateHaskell #-} 11 | {-# LANGUAGE TypeFamilies #-} 12 | {-# OPTIONS_GHC -Wall #-} 13 | {-# OPTIONS_GHC -Wno-orphans #-} 14 | {-# OPTIONS_GHC -ddump-splices #-} 15 | 16 | module KV.Protocol where 17 | 18 | import KV.Type 19 | import TypedSession.Core 20 | 21 | [kvProtocol| 22 | 23 | Label 0 24 | Branch Client ChoiceNextAction { 25 | BranchSt Stop [] 26 | Msg PStop [] Client Primary 27 | Msg BStop [] Client Backup 28 | Terminal 29 | BranchSt Put [String, String] 30 | Msg PutKV [String, String] Client Primary 31 | Msg BackupKV [String, String] Primary Backup 32 | Goto 0 33 | BranchSt Get [String] 34 | Msg GetKey [String] Client Primary 35 | Msg GetKeyResult [Maybe String] Primary Client 36 | Goto 0 37 | } 38 | 39 | |] 40 | 41 | instance Show (AnyMsg KVRole KV) where 42 | show (AnyMsg msg) = case msg of 43 | PStop -> "PStop" 44 | BStop -> "BStop" 45 | PutKV k v -> "PutKV: " <> k <> "," <> v 46 | BackupKV k v -> "BackupKV: " <> k <> "," <> v 47 | GetKey k -> "GetKey: " <> k 48 | GetKeyResult r -> "GetKeyResult: " <> show r 49 | 50 | data Request = RExit | RPut String String | RGet String deriving (Show, Read) 51 | 52 | readRequest :: IO Request 53 | readRequest = do 54 | putStrLn "Command?" 55 | line <- getLine 56 | case parseRequest line of 57 | Just t -> return t 58 | Nothing -> putStrLn "Invalid command" >> readRequest 59 | where 60 | parseRequest :: String -> Maybe Request 61 | parseRequest s = 62 | let l = words s 63 | in case l of 64 | ["GET", k] -> Just (RGet k) 65 | ["PUT", k, v] -> Just (RPut k v) 66 | "q" : _ -> Just RExit 67 | _ -> Nothing 68 | -------------------------------------------------------------------------------- /examples/src/KV/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskellQuotes #-} 2 | {-# LANGUAGE TypeApplications #-} 3 | 4 | module KV.Type where 5 | 6 | -- origin example: https://github.com/gshen42/HasChor/blob/main/examples/kvs-2-primary-backup/Main.hs 7 | import TypedSession.TH 8 | 9 | data KVRole = Client | Primary | Backup 10 | deriving (Eq, Ord, Show, Enum, Bounded) 11 | 12 | data KVBranchSt = Stop | Put | Get 13 | deriving (Eq, Ord, Show, Enum, Bounded) 14 | 15 | kvProtocol = protocol @KVRole @KVBranchSt "KV" ''KVRole ''KVBranchSt -------------------------------------------------------------------------------- /examples/src/PingPong/Peer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE NamedFieldPuns #-} 10 | {-# LANGUAGE NumericUnderscores #-} 11 | {-# LANGUAGE PolyKinds #-} 12 | {-# LANGUAGE QualifiedDo #-} 13 | {-# LANGUAGE RankNTypes #-} 14 | {-# LANGUAGE ScopedTypeVariables #-} 15 | {-# LANGUAGE TypeApplications #-} 16 | {-# LANGUAGE TypeFamilies #-} 17 | {-# OPTIONS_GHC -Wall #-} 18 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 19 | 20 | module PingPong.Peer where 21 | 22 | import Control.Monad.Class.MonadSay 23 | import Control.Monad.IO.Class (MonadIO, liftIO) 24 | import Data.Binary 25 | import Data.Binary.Get 26 | import Data.Binary.Put 27 | import qualified Data.ByteString as BS 28 | import qualified Data.ByteString.Builder.Extra as L 29 | import qualified Data.ByteString.Lazy as L 30 | import Data.IFunctor (At (..), ireturn, returnAt) 31 | import qualified Data.IFunctor as I 32 | import Data.Kind 33 | import qualified Network.Socket as Socket 34 | import qualified Network.Socket.ByteString as Socket 35 | import PingPong.Protocol 36 | import PingPong.Type 37 | import TypedSession.Codec 38 | import TypedSession.Core 39 | import TypedSession.Driver 40 | 41 | encodeMsg :: Encode PingPongRole PingPong L.ByteString 42 | encodeMsg = Encode $ \x -> runPut $ case x of 43 | Ping -> putWord8 0 44 | Pong -> putWord8 1 45 | Stop -> putWord8 2 46 | AddOne -> putWord8 3 47 | CStop -> putWord8 4 48 | Check i -> putWord8 6 >> put i 49 | CheckResult i -> putWord8 7 >> put i 50 | 51 | getAnyMsg :: Get (AnyMsg PingPongRole PingPong) 52 | getAnyMsg = do 53 | v <- getWord8 54 | case v of 55 | 0 -> pure (AnyMsg Ping) 56 | 1 -> pure (AnyMsg Pong) 57 | 2 -> pure (AnyMsg Stop) 58 | 3 -> pure (AnyMsg AddOne) 59 | 4 -> pure (AnyMsg CStop) 60 | 6 -> do 61 | i <- get 62 | pure (AnyMsg $ Check i) 63 | 7 -> do 64 | i <- get 65 | pure (AnyMsg $ CheckResult i) 66 | i -> error $ "undefined index: " ++ show i 67 | 68 | convertDecoderLBS1 69 | :: Decoder a 70 | -> (DecodeStep L.ByteString CodecFailure a) 71 | convertDecoderLBS1 = go 72 | where 73 | go :: Decoder a -> DecodeStep L.ByteString CodecFailure a 74 | go (Done tr _ a) = DecodeDone a (Just $ L.fromStrict tr) 75 | go (Fail _ _ e) = DecodeFail (CodecFailure e) 76 | go (Partial k) = DecodePartial $ \mbs -> case mbs of 77 | Nothing -> DecodeFail (CodecFailure "Peer disconnected!!") 78 | Just bs -> go (k $ Just $ L.toStrict bs) 79 | 80 | decodeMsg 81 | :: DecodeStep 82 | L.ByteString 83 | CodecFailure 84 | (AnyMsg PingPongRole PingPong) 85 | decodeMsg = convertDecoderLBS1 (runGetIncremental getAnyMsg) 86 | 87 | socketAsChannel :: Socket.Socket -> Channel IO L.ByteString 88 | socketAsChannel socket = 89 | Channel{send, recv} 90 | where 91 | send :: L.ByteString -> IO () 92 | send chunks = do 93 | Socket.sendMany socket (L.toChunks chunks) 94 | 95 | recv :: IO (Maybe L.ByteString) 96 | recv = do 97 | chunk <- Socket.recv socket L.smallChunkSize 98 | if BS.null chunk 99 | then return Nothing 100 | else return (Just (L.fromStrict chunk)) 101 | 102 | myTracer :: (MonadSay m) => String -> Tracer PingPongRole PingPong m 103 | myTracer st v = say (st <> show v) 104 | 105 | choice :: (Monad m) => Int -> ChoiceNextActionFun m 106 | choice i = 107 | if i `mod` 10 == 1 108 | then liftConstructor BranchSt_CheckVal 109 | else 110 | if i <= 50 111 | then liftConstructor BranchSt_Continue 112 | else liftConstructor BranchSt_Finish 113 | 114 | clientPeer 115 | :: (Monad m) => Int -> Peer PingPongRole PingPong Client m (At () Done) ClientStartSt 116 | clientPeer i = I.do 117 | res <- choice i 118 | case res of 119 | BranchSt_CheckVal -> I.do 120 | yield (Check i) 121 | (CheckResult _b) <- await 122 | clientPeer (i + 1) 123 | BranchSt_Continue -> I.do 124 | yield Ping 125 | Pong <- await 126 | yield AddOne 127 | clientPeer (i + 1) 128 | BranchSt_Finish -> I.do 129 | yield Stop 130 | yield CStop 131 | 132 | serverPeer 133 | :: (Monad m) => Peer PingPongRole PingPong Server m (At () Done) (ServerStartSt s) 134 | serverPeer = I.do 135 | msg <- await 136 | case msg of 137 | Ping -> I.do 138 | yield Pong 139 | serverPeer 140 | Stop -> returnAt () 141 | 142 | counterPeer 143 | :: (MonadIO m) => Int -> Peer PingPongRole PingPong Counter m (At () Done) (CounterStartSt s) 144 | counterPeer i = I.do 145 | msg <- await 146 | case msg of 147 | Check _i -> I.do 148 | yield (CheckResult True) 149 | counterPeer i 150 | AddOne -> I.do 151 | liftm $ liftIO $ putStrLn $ "counter val: " <> show i 152 | counterPeer (i + 1) 153 | CStop -> returnAt () -------------------------------------------------------------------------------- /examples/src/PingPong/Protocol.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE ExistentialQuantification #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE KindSignatures #-} 6 | {-# LANGUAGE MagicHash #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE QuasiQuotes #-} 9 | {-# LANGUAGE TemplateHaskell #-} 10 | {-# LANGUAGE TypeFamilies #-} 11 | {-# OPTIONS_GHC -Wall #-} 12 | {-# OPTIONS_GHC -Wno-orphans #-} 13 | 14 | module PingPong.Protocol where 15 | 16 | import PingPong.Type 17 | import TypedSession.Core 18 | 19 | [pingpongProtocl| 20 | 21 | Label 0 22 | Branch Client ChoiceNextAction { 23 | BranchSt CheckVal [] 24 | Msg Check [Int] Client Counter 25 | Msg CheckResult [Bool] Counter Client 26 | Goto 0 27 | BranchSt Continue [] 28 | Msg Ping [] Client Server 29 | Msg Pong [] Server Client 30 | Msg AddOne [] Client Counter 31 | Goto 0 32 | BranchSt Finish [] 33 | Msg Stop [] Client Server 34 | Msg CStop [] Client Counter 35 | Terminal 36 | } 37 | 38 | |] 39 | 40 | instance Show (AnyMsg PingPongRole PingPong) where 41 | show (AnyMsg msg) = case msg of 42 | Ping -> "Ping" 43 | Pong -> "Pong" 44 | Stop -> "Stop" 45 | AddOne -> "AddOne" 46 | CStop -> "CStop" 47 | Check i -> "CheckValue " ++ show i 48 | CheckResult b -> "CheckResult " ++ show b 49 | -------------------------------------------------------------------------------- /examples/src/PingPong/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE NamedFieldPuns #-} 10 | {-# LANGUAGE NumericUnderscores #-} 11 | {-# LANGUAGE PolyKinds #-} 12 | {-# LANGUAGE QualifiedDo #-} 13 | {-# LANGUAGE RankNTypes #-} 14 | {-# LANGUAGE ScopedTypeVariables #-} 15 | {-# LANGUAGE TemplateHaskell #-} 16 | {-# LANGUAGE TypeApplications #-} 17 | {-# LANGUAGE TypeFamilies #-} 18 | {-# OPTIONS_GHC -Wall #-} 19 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 20 | 21 | module PingPong.Type where 22 | 23 | import Language.Haskell.TH.Quote 24 | import TypedSession.TH 25 | 26 | data PingPongRole = Client | Server | Counter 27 | deriving (Show, Eq, Ord, Enum, Bounded) 28 | 29 | data PingPongBranchSt = Continue | Finish | CheckVal 30 | deriving (Show, Read, Eq, Ord, Enum, Bounded) 31 | 32 | pingpongProtocl :: QuasiQuoter 33 | pingpongProtocl = protocol @PingPongRole @PingPongBranchSt "PingPong" ''PingPongRole ''PingPongBranchSt 34 | -------------------------------------------------------------------------------- /examples/src/Ring/Peer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-# LANGUAGE LambdaCase #-} 4 | {-# LANGUAGE QualifiedDo #-} 5 | {-# OPTIONS_GHC -Wall #-} 6 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 7 | 8 | module Ring.Peer where 9 | 10 | import Control.Concurrent.Class.MonadSTM (newTVarIO) 11 | import Control.Monad (forM, void) 12 | import Control.Monad.Class.MonadFork (forkIO) 13 | import Data.IFunctor (At, returnAt) 14 | import qualified Data.IFunctor as I 15 | import qualified Data.IntMap as IntMap 16 | import GHC.Base (Any) 17 | import Ring.Protocol 18 | import Ring.Type 19 | import TypedSession.Core 20 | import TypedSession.Driver (SomeRole (..), localDriverSimple, runPeerWithDriver) 21 | 22 | choice :: Int -> ChoiceNAFun IO 23 | choice i = 24 | if i >= 3 25 | then liftConstructor BranchSt_Stop 26 | else liftConstructor BranchSt_Continue 27 | 28 | aPeer :: Int -> Peer RingRole Ring A IO (At () Done) (AStartSt) 29 | aPeer i = I.do 30 | choice i I.>>= \case 31 | BranchSt_Stop -> I.do 32 | yield BStop 33 | DStopA <- await 34 | returnAt () 35 | BranchSt_Continue -> I.do 36 | yield AB 37 | DA <- await 38 | aPeer (i + 1) 39 | 40 | bPeer :: Peer RingRole Ring B IO (At () Done) (BStartSt s) 41 | bPeer = 42 | await I.>>= \case 43 | BStop -> I.do 44 | yield CStop 45 | AB -> I.do 46 | yield BC 47 | bPeer 48 | 49 | cPeer :: Peer RingRole Ring C IO (At () Done) (CStartSt s) 50 | cPeer = 51 | await I.>>= \case 52 | CStop -> I.do 53 | yield DStop 54 | BC -> I.do 55 | yield CD 56 | cPeer 57 | 58 | dPeer :: Peer RingRole Ring D IO (At () Done) (DStartSt s) 59 | dPeer = 60 | await I.>>= \case 61 | DStop -> I.do 62 | yield DStopA 63 | CD -> I.do 64 | yield DA 65 | dPeer 66 | 67 | main :: IO () 68 | main = do 69 | let rg = [A .. D] 70 | vs <- forM rg $ \r -> do 71 | tvar <- newTVarIO IntMap.empty 72 | pure (fromEnum r, tvar) 73 | let allMap = IntMap.fromList vs 74 | driver someRole = localDriverSimple (\v -> putStrLn (show v)) allMap someRole id 75 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SB) bPeer 76 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SC) cPeer 77 | forkIO $ void $ runPeerWithDriver (driver $ SomeRole SD) dPeer 78 | void $ runPeerWithDriver (driver $ SomeRole SA) (aPeer 0) -------------------------------------------------------------------------------- /examples/src/Ring/Protocol.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | {-# LANGUAGE GADTs #-} 4 | {-# LANGUAGE KindSignatures #-} 5 | {-# LANGUAGE MultiParamTypeClasses #-} 6 | {-# LANGUAGE QuasiQuotes #-} 7 | {-# LANGUAGE TemplateHaskell #-} 8 | {-# LANGUAGE TypeFamilies #-} 9 | 10 | module Ring.Protocol where 11 | 12 | import Ring.Type 13 | import TypedSession.Core 14 | 15 | [ringPrtocol| 16 | 17 | Label 0 18 | Branch A ChoiceNA { 19 | BranchSt Stop [] 20 | Msg BStop [] A B 21 | Msg CStop [] B C 22 | Msg DStop [] C D 23 | Msg DStopA [] D A 24 | Terminal 25 | BranchSt Continue [] 26 | Msg AB [] A B 27 | Msg BC [] B C 28 | Msg CD [] C D 29 | Msg DA [] D A 30 | Goto 0 31 | } 32 | 33 | |] 34 | 35 | instance Show (AnyMsg RingRole Ring) where 36 | show (AnyMsg msg) = case msg of 37 | BStop -> "BStop" 38 | CStop -> "CStop" 39 | DStop -> "DStop" 40 | DStopA -> "DStopA" 41 | AB -> "AB" 42 | BC -> "BC" 43 | CD -> "CD" 44 | DA -> "DA" -------------------------------------------------------------------------------- /examples/src/Ring/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskellQuotes #-} 2 | {-# LANGUAGE TypeApplications #-} 3 | 4 | module Ring.Type where 5 | 6 | import TypedSession.TH 7 | 8 | data RingRole = A | B | C | D 9 | deriving (Eq, Ord, Show, Enum, Bounded) 10 | 11 | data RingBranchSt = Stop | Continue 12 | deriving (Eq, Ord, Show, Enum, Bounded) 13 | 14 | ringPrtocol = protocol @RingRole @RingBranchSt "Ring" ''RingRole ''RingBranchSt 15 | -------------------------------------------------------------------------------- /src/Data/IFunctor.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | {-# LANGUAGE PolyKinds #-} 3 | {-# LANGUAGE RankNTypes #-} 4 | {-# LANGUAGE StandaloneKindSignatures #-} 5 | {-# LANGUAGE TypeFamilies #-} 6 | {-# LANGUAGE TypeOperators #-} 7 | 8 | module Data.IFunctor where 9 | 10 | import Data.Data 11 | import Data.Kind 12 | 13 | {- | 14 | Singletons are not used here. I am not sure if singletons can generate the instances I need. 15 | 16 | Here is an example: Ping-Pong 17 | 18 | @ 19 | data PingPong 20 | = S0 [Bool] 21 | | S1 22 | | S2 [Bool] 23 | | End 24 | 25 | data SPingPong :: PingPong -> Type where 26 | SS0 :: SPingPong (S0 b) 27 | SS1 :: SPingPong S1 28 | SS2 :: SPingPong (S2 b) 29 | SEnd :: SPingPong End 30 | @ 31 | 32 | Note here /SS0 :: SPingPong (S0 b)/ 33 | 34 | Using singletons will generate /SS0 :: Sing b -> SPingPong (S0 b)/ which is not what I need. 35 | 36 | Please note the following example: 37 | 38 | @ 39 | serverPeer :: 40 | (Monad m) => Peer Role PingPong Server m (At () Done) (S0 s) 41 | serverPeer = I.do 42 | -- The server is in a state (S0 s) while it is awaiting a message, 43 | -- and its state is indeterminate until it receives a message. 44 | -- SS0 :: SPingPong (S0 b) correctly indicates this indeterminacy. 45 | Recv msg <- await 46 | case msg of 47 | Ping -> I.do 48 | yield Pong 49 | serverPeer 50 | Stop -> returnAt () 51 | @ 52 | -} 53 | type family Sing :: k -> Type 54 | 55 | type SingI :: forall {k}. k -> Constraint 56 | 57 | {- | 58 | 59 | example:Ping-Pong 60 | 61 | @ 62 | type instance Sing = SPingPong 63 | 64 | instance SingI (S0 b) where 65 | sing = SS0 66 | 67 | instance SingI S1 where 68 | sing = SS1 69 | 70 | instance SingI (S2 b) where 71 | sing = SS2 72 | 73 | instance SingI End where 74 | sing = SEnd 75 | @ 76 | -} 77 | class SingI a where 78 | sing :: Sing a 79 | 80 | infixr 0 ~> 81 | 82 | type f ~> g = forall x. f x -> g x 83 | 84 | class IFunctor f where 85 | imap :: (a ~> b) -> f a ~> f b 86 | 87 | {- | McBride Indexed Monads 88 | 89 | Here's Edward Kmett's [introduction to Indexed Monads](https://stackoverflow.com/questions/28690448/what-is-indexed-monad). 90 | 91 | As he said, there are at least three indexed monads: 92 | 93 | 94 | * Bob Atkey 95 | 96 | @ 97 | class IMonad m where 98 | ireturn :: a -> m i i a 99 | ibind :: m i j a -> (a -> m j k b) -> m i k b 100 | @ 101 | 102 | * Conor McBride 103 | 104 | @ 105 | type a ~> b = forall i. a i -> b i 106 | 107 | class IMonad m where 108 | ireturn :: a ~> m a 109 | ibind :: (a ~> m b) -> (m a ~> m b) 110 | @ 111 | 112 | * Dominic Orchard 113 | 114 | No detailed description, just a link to this [lecture](https://github.com/dorchard/effect-monad/blob/master/docs/ixmonad-fita14.pdf)。 115 | 116 | I use the McBride Indexed Monad, the earliest paper [here](https://personal.cis.strath.ac.uk/conor.mcbride/Kleisli.pdf). 117 | 118 | The following is my understanding of (\~>): through GADT, let the value contain type information, 119 | and then use ((\~>), pattern match) to pass the type to subsequent functions 120 | 121 | @ 122 | data V = A | B 123 | 124 | data SV :: V -> Type where -- GADT, let the value contain type information 125 | SA :: SV A 126 | SB :: SV B 127 | 128 | data SV1 :: V -> Type where 129 | SA1 :: SV1 A 130 | SB1 :: SV1 B 131 | 132 | fun :: SV ~> SV1 -- type f ~> g = forall x. f x -> g x 133 | fun sv = case sv of -- x is arbitrary but f, g must have the same x 134 | SA -> SA1 -- Pass concrete type state to subsequent functions via pattern matching 135 | SB -> SB1 136 | 137 | 138 | class (IFunctor m) => IMonad m where 139 | ireturn :: a ~> m a 140 | ibind :: (a ~> m b) -- The type information contained in a will be passed to (m b), 141 | -- which is exactly what we need: external input has an impact on the type! 142 | -> m a ~> m b 143 | @ 144 | -} 145 | class (IFunctor m) => IMonad m where 146 | ireturn :: a ~> m a 147 | ibind :: (a ~> m b) -> m a ~> m b 148 | 149 | class (IMonad m) => IMonadFail m where 150 | fail :: String -> m a ix 151 | 152 | data At :: Type -> k -> k -> Type where 153 | At :: a -> At a k k 154 | deriving (Typeable) 155 | 156 | (>>=) :: (IMonad (m :: (x -> Type) -> x -> Type)) => m a ix -> (a ~> m b) -> m b ix 157 | m >>= f = ibind f m 158 | 159 | (>>) :: (IMonad (m :: (x -> Type) -> x -> Type)) => m (At a j) i -> m b j -> m b i 160 | m >> f = ibind (\(At _) -> f) m 161 | 162 | returnAt :: (IMonad m) => a -> m (At a k) k 163 | returnAt = ireturn . At 164 | -------------------------------------------------------------------------------- /src/TypedSession/Codec.hs: -------------------------------------------------------------------------------- 1 | -- This part of the code comes from typed-protocols, I modified a few things. 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE NamedFieldPuns #-} 6 | {-# LANGUAGE PolyKinds #-} 7 | {-# LANGUAGE QuantifiedConstraints #-} 8 | {-# LANGUAGE RankNTypes #-} 9 | {-# LANGUAGE ScopedTypeVariables #-} 10 | {-# LANGUAGE TypeFamilies #-} 11 | {-# LANGUAGE UndecidableInstances #-} 12 | 13 | module TypedSession.Codec where 14 | 15 | import Control.Exception (Exception) 16 | import TypedSession.Core 17 | 18 | {- | 19 | Function to encode Msg into bytes. 20 | -} 21 | newtype Encode role' ps bytes = Encode 22 | { encode 23 | :: forall (send :: role') (recv :: role') (st :: ps) (st' :: ps) (st'' :: ps) 24 | . Msg role' ps st send st' recv st'' 25 | -> bytes 26 | } 27 | 28 | {- | 29 | Incremental decoding function. 30 | -} 31 | newtype Decode role' ps failure bytes = Decode 32 | { decode :: DecodeStep bytes failure (AnyMsg role' ps) 33 | } 34 | 35 | {- | 36 | Generic incremental decoder constructor, you need to convert specific incremental decoders to it. 37 | -} 38 | data DecodeStep bytes failure a 39 | = DecodePartial (Maybe bytes -> (DecodeStep bytes failure a)) 40 | | DecodeDone a (Maybe bytes) 41 | | DecodeFail failure 42 | 43 | data CodecFailure 44 | = CodecFailureOutOfInput 45 | | CodecFailure String 46 | deriving (Eq, Show) 47 | 48 | instance Exception CodecFailure 49 | 50 | {- | 51 | Bottom functions for sending and receiving bytes. 52 | -} 53 | data Channel m bytes = Channel 54 | { send :: bytes -> m () 55 | , recv :: m (Maybe bytes) 56 | } 57 | 58 | {- | 59 | Generic incremental decoding function. 60 | -} 61 | runDecoderWithChannel 62 | :: (Monad m) 63 | => Channel m bytes 64 | -> Maybe bytes 65 | -> DecodeStep bytes failure a 66 | -> m (Either failure (a, Maybe bytes)) 67 | runDecoderWithChannel Channel{recv} = go 68 | where 69 | go _ (DecodeDone x trailing) = return (Right (x, trailing)) 70 | go _ (DecodeFail failure) = return (Left failure) 71 | go Nothing (DecodePartial k) = recv >>= pure . k >>= go Nothing 72 | go (Just trailing) (DecodePartial k) = (pure . k) (Just trailing) >>= go Nothing 73 | -------------------------------------------------------------------------------- /src/TypedSession/Core.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE GADTs #-} 4 | {-# LANGUAGE KindSignatures #-} 5 | {-# LANGUAGE LambdaCase #-} 6 | {-# LANGUAGE MultiParamTypeClasses #-} 7 | {-# LANGUAGE PolyKinds #-} 8 | {-# LANGUAGE RankNTypes #-} 9 | {-# LANGUAGE ScopedTypeVariables #-} 10 | {-# LANGUAGE StandaloneKindSignatures #-} 11 | {-# LANGUAGE TypeApplications #-} 12 | {-# LANGUAGE TypeFamilies #-} 13 | {-# LANGUAGE TypeOperators #-} 14 | 15 | module TypedSession.Core where 16 | 17 | import Control.Concurrent.Class.MonadSTM (MonadSTM (..)) 18 | import Data.IFunctor 19 | import Data.IntMap (IntMap) 20 | import Data.Kind 21 | 22 | {- | 23 | 24 | typed-session is a communication framework. 25 | The messages received from the outside will be placed in MsgCache. 26 | When interpreting the Peer, (Sing (r :: s)) will be generated according to the Peer's status. 27 | SingToInt can convert Sing (r :: s) to Int. 28 | Depending on this Int value, the required Msg is finally found from MsgCache. 29 | 30 | In the process of multi-role communication, a message cache structure like MsgCache is needed. 31 | 32 | Consider the following scenario 33 | 34 | @ 35 | s1 s1 s2 Initial state 36 | ------------ 37 | a -> b a sends message MsgA to b 38 | ------------ 39 | s3 s2 s2 State after sending 40 | ------------ 41 | b <- c c sends message MsgC to b 42 | ------------ 43 | s3 s4 s5 State after sending 44 | @ 45 | 46 | For b, due to the influence of network transmission, it cannot guarantee that it can receive MsgA first and then MsgC. 47 | If it receives MsgC first, it will directly put it in MsgCache and continue to wait until 48 | MsgA arrives, then it will start to process MsgA first and then MsgC. 49 | 50 | In general, dataToTag# is used directly here. 51 | 52 | Example: 53 | 54 | @ 55 | instance SingToInt Role where 56 | singToInt x = I# (dataToTag# x) 57 | 58 | instance SingToInt PingPong where 59 | singToInt x = I# (dataToTag# x) 60 | @ 61 | -} 62 | class SingToInt s where 63 | singToInt :: Sing (r :: s) -> Int 64 | 65 | {- | 66 | 67 | Describes the type class of Msg. The core of typed-session. 68 | 69 | @ 70 | type Done :: ps 71 | @ 72 | 73 | Describe the state of each role when it terminates. 74 | 75 | @ 76 | data 77 | Msg 78 | role' 79 | ps 80 | (fromSt :: ps) 81 | (sender :: role') 82 | (senderNewSt :: ps) 83 | (receiver :: role') 84 | (receiverNewSt :: ps) 85 | @ 86 | * role': the type of the role. 87 | * ps: the type of the state machine. 88 | * ​​fromSt: when sending a message, the sender is in this state, 89 | where the receiver may be in this state, or a more generalized state related to this state. 90 | For example, the sender is in state (S1 [True]), and the receiver is in state (S1 s). 91 | * sender: the role that sends the message 92 | * senderNewSt: the state of the role after sending the message 93 | * receiver: the role that receives the message 94 | * receiverNewSt: the state of the role after receiving the message 95 | 96 | There are two principles to follow when designing the state of Msg: 97 | 98 | 1. When sending a message, the sender and receiver must be in the same state. Here the receiver may be in a more generalized state related to the state. 99 | For example, the sender is in state (S1 [True]), and the receiver is in state (S1 s). 100 | 101 | 2. The same state can only be used for the same pair of receiver and sender. 102 | 103 | For example, in the following example, state s1 is used for both (a -> b) and (b -> c), which is wrong. 104 | 105 | @ 106 | s1 s1 s1 107 | a -> b 108 | s2 s1 s1 109 | b -> c 110 | s2 s4 s5 111 | @ 112 | -} 113 | class (SingToInt role', SingToInt ps) => Protocol role' ps where 114 | type Done :: ps 115 | data 116 | Msg 117 | role' 118 | ps 119 | (fromSt :: ps) 120 | (sender :: role') 121 | (senderNewSt :: ps) 122 | (receiver :: role') 123 | (receiverNewSt :: ps) 124 | 125 | {- | 126 | Messages received from the outside are placed in MsgCache. When interpreting 127 | Peer will use the Msg in MsgCache. 128 | -} 129 | type MsgCache role' ps n = TVar n (IntMap (AnyMsg role' ps)) 130 | 131 | {- | 132 | Packaging of Msg, shielding part of the type information, mainly used for serialization. 133 | -} 134 | data AnyMsg role' ps where 135 | AnyMsg 136 | :: ( SingI recv 137 | , SingI st 138 | , SingToInt role' 139 | , SingToInt ps 140 | ) 141 | => Msg role' ps st send st' recv st'' 142 | -> AnyMsg role' ps 143 | 144 | msgFromStSing 145 | :: forall role' ps st send recv st' st'' 146 | . (SingI recv, SingI st) 147 | => Msg role' ps st send st' recv st'' 148 | -> Sing st 149 | msgFromStSing _ = sing @st 150 | 151 | {- | 152 | Core Ast, all we do is build this Ast and then interpret it. 153 | 154 | @ 155 | IReturn :: ia st -> Peer role' ps r m ia st 156 | @ 157 | IReturn indicates the termination of the continuation. 158 | 159 | @ 160 | LiftM :: m (Peer role' ps r m ia st') -> Peer role' ps r m ia st 161 | @ 162 | 163 | Liftm can transform state st to any state st'. 164 | It looks a bit strange, as if it is a constructor that is not constrained by the Msg type. 165 | Be careful when using it, it is a type breakpoint. 166 | But some state transition functions need it, which can make the code more flexible. 167 | Be very careful when using it! 168 | 169 | @ 170 | Yield 171 | :: ( SingI recv 172 | , SingI from 173 | , SingToInt ps 174 | ) 175 | => Msg role' ps from '(send, sps) '(recv, rps) 176 | -> Peer role' ps send m ia sps 177 | -> Peer role' ps send m ia from 178 | @ 179 | Yield represents sending a message. Note that the Peer status changes from `from` to `sps`. 180 | 181 | @ 182 | Await 183 | :: ( SingI recv 184 | , SingI from 185 | , SingToInt ps 186 | ) 187 | => (Msg role' ps from send sps recv ~> Peer role' ps recv m ia) 188 | -> Peer role' ps recv m ia from 189 | @ 190 | 191 | Await represents receiving messages. 192 | Different messages will lead to different states. 193 | The state is passed to the next behavior through (~>). 194 | -} 195 | data Peer role' ps (r :: role') (m :: Type -> Type) (ia :: ps -> Type) (st :: ps) where 196 | IReturn :: ia st -> Peer role' ps r m ia st 197 | LiftM :: m (Peer role' ps r m ia st') -> Peer role' ps r m ia st 198 | Yield 199 | :: ( SingI recv 200 | , SingI from 201 | , SingToInt ps 202 | ) 203 | => Msg role' ps from send sps recv rps 204 | -> Peer role' ps send m ia sps 205 | -> Peer role' ps send m ia from 206 | Await 207 | :: ( SingI recv 208 | , SingI from 209 | , SingToInt ps 210 | ) 211 | => (Msg role' ps from send sps recv ~> Peer role' ps recv m ia) 212 | -> Peer role' ps recv m ia from 213 | 214 | instance (Functor m) => IMonadFail (Peer role' ps r m) where 215 | fail = error 216 | 217 | instance (Functor m) => IFunctor (Peer role' ps r m) where 218 | imap f = \case 219 | IReturn ia -> IReturn (f ia) 220 | LiftM f' -> LiftM (fmap (imap f) f') 221 | Yield ms cont -> Yield ms (imap f cont) 222 | Await cont -> Await (imap f . cont) 223 | 224 | instance (Functor m) => IMonad (Peer role' ps r m) where 225 | ireturn = IReturn 226 | ibind f = \case 227 | IReturn ia -> (f ia) 228 | LiftM f' -> LiftM (fmap (ibind f) f') 229 | Yield ms cont -> Yield ms (ibind f cont) 230 | Await cont -> Await (ibind f . cont) 231 | 232 | {- | 233 | Send a message, the Peer status changes from `from` to `sps`. 234 | -} 235 | yield 236 | :: ( Functor m 237 | , SingI recv 238 | , SingI from 239 | , SingToInt ps 240 | ) 241 | => Msg role' ps from send sps recv rps 242 | -> Peer role' ps send m (At () sps) from 243 | yield msg = Yield msg (returnAt ()) 244 | 245 | {- | 246 | Receiving Messages. 247 | -} 248 | await 249 | :: ( Functor m 250 | , SingI recv 251 | , SingI from 252 | , SingToInt ps 253 | ) 254 | => Peer role' ps recv m (Msg role' ps from send sps recv) from 255 | await = Await ireturn 256 | 257 | {- | 258 | Lift any m to Peer role' ps r m, which is an application of LiftM. 259 | Note that the state of `ts` has not changed. 260 | -} 261 | liftm :: (Functor m) => m a -> Peer role' ps r m (At a ts) ts 262 | liftm m = LiftM (returnAt <$> m) 263 | 264 | liftConstructor :: (Applicative m) => ia st' -> Peer role' k r m ia st 265 | liftConstructor a = LiftM $ pure $ ireturn a -------------------------------------------------------------------------------- /src/TypedSession/Driver.hs: -------------------------------------------------------------------------------- 1 | -- This part of the code comes from typed-protocols, I modified a few things. 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE NamedFieldPuns #-} 6 | {-# LANGUAGE NumericUnderscores #-} 7 | {-# LANGUAGE PolyKinds #-} 8 | {-# LANGUAGE RankNTypes #-} 9 | {-# LANGUAGE ScopedTypeVariables #-} 10 | {-# LANGUAGE TypeApplications #-} 11 | {-# LANGUAGE TypeOperators #-} 12 | {-# LANGUAGE UndecidableInstances #-} 13 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 14 | 15 | {- | 16 | Schematic diagram of the communication structure of three roles through typed-session: 17 | 18 | <> 19 | 20 | Some explanations for this diagram: 21 | 22 | 1. Roles are connected through channels, and there are many types of channels, such as channels established through TCP or channels established through TMVar. 23 | 24 | 2. Each role has a Peer thread, in which the Peer runs. 25 | 26 | 3. Each role has one or more decode threads, and the decoded Msgs are placed in the MsgCache. 27 | -} 28 | module TypedSession.Driver where 29 | 30 | import Control.Concurrent.Class.MonadSTM 31 | import Control.Monad.Class.MonadFork (MonadFork (killThread), forkIO) 32 | import Control.Monad.Class.MonadThrow (MonadThrow, throwIO) 33 | import Control.Monad.Class.MonadTimer (MonadDelay, threadDelay) 34 | import Data.Data (Typeable) 35 | import Data.IFunctor (At (..), Sing, SingI (sing)) 36 | import qualified Data.IFunctor as I 37 | import Data.IntMap (IntMap) 38 | import qualified Data.IntMap as IntMap 39 | import Data.Traversable (for) 40 | import GHC.Exception (Exception) 41 | import TypedSession.Codec 42 | import TypedSession.Core 43 | import Unsafe.Coerce (unsafeCoerce) 44 | 45 | {- | 46 | Contains two functions sendMsg, recvMsg. 47 | runPeerWithDriver uses them to send and receive Msg. 48 | -} 49 | data Driver role' ps m 50 | = Driver 51 | { sendMsg 52 | :: forall (send :: role') (recv :: role') (st :: ps) (st' :: ps) (st'' :: ps) 53 | . ( SingI recv 54 | , SingI st 55 | , SingToInt ps 56 | , SingToInt role' 57 | ) 58 | => Sing recv 59 | -> Msg role' ps st send st' recv st'' 60 | -> m () 61 | , recvMsg 62 | :: forall (st' :: ps) 63 | . (SingToInt ps) 64 | => Sing st' 65 | -> m (AnyMsg role' ps) 66 | , terminalDecodeThreads :: [m ()] 67 | } 68 | 69 | {- | 70 | Interpret Peer. 71 | -} 72 | runPeerWithDriver 73 | :: forall role' ps (r :: role') (st :: ps) m a 74 | . ( Monad m 75 | , (SingToInt role') 76 | ) 77 | => Driver role' ps m 78 | -> Peer role' ps r m (At a Done) st 79 | -> m a 80 | runPeerWithDriver Driver{sendMsg, recvMsg, terminalDecodeThreads} peer = do 81 | a <- go peer 82 | sequence_ terminalDecodeThreads 83 | pure a 84 | where 85 | go 86 | :: forall st' 87 | . Peer role' ps r m (At a Done) st' 88 | -> m a 89 | go (IReturn (At a)) = pure a 90 | go (LiftM k) = k >>= go 91 | go (Yield (msg :: Msg role' ps st' r sps recv rps) k) = do 92 | sendMsg (sing @recv) msg 93 | go k 94 | go (Await (k :: (Msg role' ps st' send sps r I.~> Peer role' ps r m ia))) = do 95 | AnyMsg msg <- recvMsg (sing @st') 96 | go (k $ unsafeCoerce msg) 97 | 98 | {- | 99 | A wrapper around AnyMsg that represents sending and receiving Msg. 100 | -} 101 | data TraceSendRecv role' ps where 102 | TraceSendMsg :: AnyMsg role' ps -> TraceSendRecv role' ps 103 | TraceRecvMsg :: AnyMsg role' ps -> TraceSendRecv role' ps 104 | 105 | instance (Show (AnyMsg role' ps)) => Show (TraceSendRecv role' ps) where 106 | show (TraceSendMsg msg) = "Send " ++ show msg 107 | show (TraceRecvMsg msg) = "Recv " ++ show msg 108 | 109 | {- | 110 | Similar to the log function, used to print received or sent messages. 111 | -} 112 | type Tracer role' ps m = TraceSendRecv role' ps -> m () 113 | 114 | {- | 115 | The default trace function. It simply ignores everything. 116 | -} 117 | nullTracer :: (Monad m) => a -> m () 118 | nullTracer _ = pure () 119 | 120 | {- | 121 | ConnChannels aggregates the multiple connect channels together. 122 | -} 123 | type ConnChannels role' m bytes = [(SomeRole role', Channel m bytes)] 124 | 125 | data NotConnect role' = NotConnect role' 126 | deriving (Show) 127 | 128 | instance (Show role', Typeable role') => Exception (NotConnect role') 129 | 130 | data SomeRole role' = forall (r :: role'). (SingToInt role') => SomeRole (Sing r) 131 | 132 | {- | 133 | 134 | Build Driver through ConnChannels. 135 | Here we need some help from other functions: 136 | 137 | 1. `Tracer role' ps n` is similar to the log function, used to print received or sent messages. 138 | 2. `Encode role' ps` bytes encoding function, converts Msg into bytes. 139 | 3. `Decode role' ps failure bytes` bytes decode function, converts bytes into Msg. 140 | 4. `forall a. n a -> m a` This is a bit complicated, I will explain it in detail below. 141 | 142 | I see Peer as three layers: 143 | 144 | 1. `Peer` upper layer, meets the requirements of McBride Indexed Monad, uses do syntax construction, has semantic checks, and is interpreted to the second layer m through runPeerWithDriver. 145 | 2. `m` middle layer, describes the business requirements in this layer, and converts the received Msg into specific business actions. 146 | 3. `n` bottom layer, responsible for receiving and sending bytes. It can have multiple options such as IO or IOSim. Using IOSim can easily test the code. 147 | -} 148 | driverSimple 149 | :: forall role' ps failure bytes m n 150 | . ( Monad m 151 | , Monad n 152 | , MonadSTM n 153 | , MonadFork n 154 | , MonadDelay n 155 | , MonadThrow n 156 | , SingToInt role' 157 | , Enum role' 158 | , Show role' 159 | , Typeable role' 160 | , Exception failure 161 | ) 162 | => Tracer role' ps n 163 | -> Encode role' ps bytes 164 | -> Decode role' ps failure bytes 165 | -> ConnChannels role' n bytes 166 | -> (forall a. n a -> m a) 167 | -> n (Driver role' ps m) 168 | driverSimple 169 | tracer 170 | Encode{encode} 171 | decodeV 172 | connChannels 173 | liftFun = do 174 | msgCache <- newTVarIO IntMap.empty 175 | ths <- for connChannels $ \(_, channel) -> forkIO $ decodeLoop Nothing decodeV channel msgCache 176 | let terminalDecodeThreads = map (\tid -> liftFun $ killThread tid) ths 177 | pure $ Driver{sendMsg, recvMsg = recvMsg' msgCache, terminalDecodeThreads} 178 | where 179 | sendMap = IntMap.fromList $ fmap (\(SomeRole r, c) -> (singToInt r, send c)) connChannels 180 | sendMsg 181 | :: forall (send :: role') (recv :: role') (from :: ps) (st :: ps) (st1 :: ps) 182 | . ( SingI recv 183 | , SingI from 184 | , SingToInt ps 185 | , SingToInt role' 186 | ) 187 | => Sing recv 188 | -> Msg role' ps from send st recv st1 189 | -> m () 190 | sendMsg role msg = liftFun $ do 191 | let recvKey = singToInt role 192 | case IntMap.lookup recvKey sendMap of 193 | Nothing -> do 194 | let recvRole = toEnum @role' recvKey 195 | throwIO (NotConnect recvRole) 196 | Just sendFun -> sendFun (encode msg) 197 | tracer (TraceSendMsg (AnyMsg msg)) 198 | 199 | recvMsg' 200 | :: forall (st' :: ps) 201 | . (SingToInt ps) 202 | => MsgCache role' ps n 203 | -> Sing st' 204 | -> m (AnyMsg role' ps) 205 | recvMsg' msgCache sst' = do 206 | let singInt = singToInt sst' 207 | liftFun $ do 208 | anyMsg <- atomically $ do 209 | agencyMsg <- readTVar msgCache 210 | case IntMap.lookup singInt agencyMsg of 211 | Nothing -> retry 212 | Just v -> do 213 | writeTVar msgCache (IntMap.delete singInt agencyMsg) 214 | pure v 215 | tracer (TraceRecvMsg (anyMsg)) 216 | pure anyMsg 217 | 218 | {- | 219 | decode loop, usually in a separate thread. 220 | 221 | The decoded Msg is placed in MsgCache. 222 | 223 | @ 224 | data Msg role' ps (from :: ps) (sendAndSt :: (role', ps)) (recvAndSt :: (role', ps)) 225 | @ 226 | Note that when placing a new Msg in MsgCache, if a Msg with the same `from` already exists in MsgCache, the decoding process will be blocked, 227 | until that Msg is consumed before placing the new Msg in MsgCache. 228 | 229 | This usually happens when the efficiency of Msg generation is greater than the efficiency of consumption. 230 | -} 231 | decodeLoop 232 | :: (Exception failure, MonadDelay n, MonadSTM n, MonadThrow n) 233 | => Maybe bytes 234 | -> Decode role' ps failure bytes 235 | -> Channel n bytes 236 | -> MsgCache role' ps n 237 | -> n () 238 | decodeLoop mbt d@Decode{decode} channel tvar = do 239 | result <- runDecoderWithChannel channel mbt decode 240 | case result of 241 | Right (AnyMsg msg, mbt') -> do 242 | let agencyInt = singToInt $ msgFromStSing msg 243 | atomically $ do 244 | agencyMsg <- readTVar tvar 245 | case IntMap.lookup agencyInt agencyMsg of 246 | Nothing -> writeTVar tvar (IntMap.insert agencyInt (AnyMsg msg) agencyMsg) 247 | Just _v -> retry 248 | decodeLoop mbt' d channel tvar 249 | Left failure -> do 250 | threadDelay 1_000_000 251 | throwIO failure 252 | 253 | localDriverSimple 254 | :: forall role' ps m n 255 | . ( Monad m 256 | , Monad n 257 | , MonadSTM n 258 | , Enum role' 259 | , MonadThrow n 260 | , Show role' 261 | , Typeable role' 262 | ) 263 | => Tracer role' ps n 264 | -> IntMap (MsgCache role' ps n) 265 | -> SomeRole role' 266 | -> (forall a. n a -> m a) 267 | -> Driver role' ps m 268 | localDriverSimple tracer allMsgCache (SomeRole r) liftFun = 269 | Driver{sendMsg, recvMsg = recvMsg' (allMsgCache IntMap.! (singToInt r)), terminalDecodeThreads = []} 270 | where 271 | sendMsg 272 | :: forall (send :: role') (recv :: role') (from :: ps) (st :: ps) (st1 :: ps) 273 | . ( SingI recv 274 | , SingI from 275 | , SingToInt ps 276 | , SingToInt role' 277 | ) 278 | => Sing recv 279 | -> Msg role' ps from send st recv st1 280 | -> m () 281 | sendMsg role msg = liftFun $ do 282 | let recvKey = singToInt role 283 | case IntMap.lookup (singToInt role) allMsgCache of 284 | Nothing -> do 285 | let recvRole = toEnum @role' recvKey 286 | throwIO (NotConnect recvRole) 287 | Just ttvar -> atomically $ do 288 | agencyMsg <- readTVar ttvar 289 | let singInt = singToInt (sing @from) 290 | case IntMap.lookup singInt agencyMsg of 291 | Nothing -> writeTVar ttvar (IntMap.insert singInt (AnyMsg msg) agencyMsg) 292 | Just _v -> retry 293 | tracer (TraceSendMsg (AnyMsg msg)) 294 | 295 | recvMsg' 296 | :: forall (st' :: ps) 297 | . (SingToInt ps) 298 | => MsgCache role' ps n 299 | -> Sing st' 300 | -> m (AnyMsg role' ps) 301 | recvMsg' msgCache sst' = do 302 | let singInt = singToInt sst' 303 | liftFun $ do 304 | anyMsg <- atomically $ do 305 | agencyMsg <- readTVar msgCache 306 | case IntMap.lookup singInt agencyMsg of 307 | Nothing -> retry 308 | Just v -> do 309 | writeTVar msgCache (IntMap.delete singInt agencyMsg) 310 | pure v 311 | tracer (TraceRecvMsg (anyMsg)) 312 | pure anyMsg -------------------------------------------------------------------------------- /src/TypedSession/TH.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE KindSignatures #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE NamedFieldPuns #-} 9 | {-# LANGUAGE PolyKinds #-} 10 | {-# LANGUAGE RankNTypes #-} 11 | {-# LANGUAGE ScopedTypeVariables #-} 12 | {-# LANGUAGE TemplateHaskell #-} 13 | {-# LANGUAGE TypeApplications #-} 14 | {-# OPTIONS_GHC -Wno-unused-imports #-} 15 | {-# OPTIONS_GHC -split-sections #-} 16 | 17 | module TypedSession.TH (protocol, protocol') where 18 | 19 | import Control.Monad (forM, when) 20 | import Data.Either (fromRight) 21 | import Data.IFunctor (Sing, SingI (..)) 22 | import Data.Kind 23 | import qualified Data.Set as Set 24 | import Data.Traversable (for) 25 | import GHC.Exts (DataToTag (..), Int (..)) 26 | import Language.Haskell.TH hiding (Type) 27 | import qualified Language.Haskell.TH as TH 28 | import Language.Haskell.TH.Quote 29 | import TypedSession.Core (SingToInt (..)) 30 | import qualified TypedSession.Core as TSC 31 | import TypedSession.State.Parser (runProtocolParser) 32 | import TypedSession.State.Pipeline (PipeResult (..), genGraph, pipe) 33 | import TypedSession.State.Render 34 | import TypedSession.State.Type (BranchSt (..), Creat, MsgOrLabel (..), MsgT1, Protocol (..), ProtocolError, T (..)) 35 | 36 | roleDecs :: Name -> Q [Dec] 37 | roleDecs name = do 38 | res <- reify name 39 | case res of 40 | TyConI (DataD [] dName [] Nothing cons _) -> do 41 | aVar <- newName "a" 42 | xVar <- newName "x" 43 | let 44 | addPerfixS :: Name -> Name 45 | addPerfixS vname = 46 | let n = (nameBase vname) 47 | in mkName ("S" <> n) 48 | 49 | roleSingleton = 50 | [ DataD 51 | [] 52 | (addPerfixS dName) 53 | [KindedTV aVar BndrReq (ConT dName)] 54 | Nothing 55 | [GadtC [addPerfixS n] [] (AppT (ConT (addPerfixS dName)) (PromotedT n)) | NormalC n [] <- cons] 56 | [] 57 | ] 58 | singToSRole = [TySynInstD (TySynEqn Nothing (ConT ''Sing) (ConT (addPerfixS dName)))] 59 | instanceSingI = 60 | [ InstanceD 61 | Nothing 62 | [] 63 | (AppT (ConT ''SingI) (PromotedT n)) 64 | [FunD 'sing [Clause [] (NormalB (ConE (addPerfixS n))) []]] 65 | | NormalC n [] <- cons 66 | ] 67 | instanceSingToInt = 68 | [ InstanceD 69 | Nothing 70 | [] 71 | (AppT (ConT ''SingToInt) (ConT name)) 72 | [ FunD 73 | 'singToInt 74 | [Clause [VarP xVar] (NormalB (AppE (ConE 'I#) (AppE (VarE 'dataToTag#) (VarE xVar)))) []] 75 | ] 76 | ] 77 | pure $ roleSingleton ++ singToSRole ++ instanceSingI ++ instanceSingToInt 78 | _ -> error $ "Name: " ++ show name ++ " is not a data constructor" 79 | 80 | protDecsAndMsgDecs :: forall r bst. (Show r, Show bst, Enum r, Bounded r) => String -> Name -> Name -> PipeResult r bst -> Q [Dec] 81 | protDecsAndMsgDecs protN roleName bstName PipeResult{msgT, msgT1, dnySet, stList, branchResultTypeInfo, branchFunList, allMsgBATypes} = do 82 | let protName = mkName protN 83 | protSName = mkName ("S" <> protN) 84 | mkSiName i = mkName $ "S" <> show i 85 | mkSSiName i = mkName $ "SS" <> show i 86 | genConstr i = 87 | if i == -1 88 | then NormalC (mkName "End") [] 89 | else 90 | if i `Set.member` dnySet 91 | then 92 | NormalC 93 | (mkSiName i) 94 | [(Bang NoSourceUnpackedness NoSourceStrictness, ConT bstName)] 95 | else NormalC (mkSiName i) [] 96 | -- generate protocol data type 97 | dataProt = [DataD [] protName [] Nothing [genConstr i | i <- stList] []] 98 | 99 | let tAnyToType :: Name -> T bst -> TH.Type 100 | tAnyToType s = \case 101 | TNum i -> PromotedT (mkSiName i) 102 | BstList i bst -> AppT (PromotedT (mkSiName i)) (PromotedT (mkName (show bst))) 103 | TAny i -> AppT (PromotedT (mkSiName i)) (VarT s) 104 | TEnd -> PromotedT $ mkName "End" 105 | mkArgs args = 106 | [ ( Bang NoSourceUnpackedness NoSourceStrictness 107 | , case ag of 108 | [] -> error "np" 109 | (x : xs) -> foldl' AppT (ConT (mkName x)) (map (ConT . mkName) xs) 110 | ) 111 | | ag <- args 112 | ] 113 | typeListT :: [TH.Type] -> TH.Type 114 | typeListT = foldl1 AppT 115 | 116 | isTAny :: T bst -> Bool 117 | isTAny = \case 118 | TAny _ -> True 119 | _ -> False 120 | 121 | sVar <- newName "s" 122 | aVar <- newName "a" 123 | let genSConstr i = 124 | if i == -1 125 | then 126 | GadtC [mkName "SEnd"] [] (AppT (ConT protSName) (PromotedT (mkName "End"))) 127 | else 128 | if i `Set.member` dnySet 129 | then 130 | ForallC 131 | [KindedTV sVar SpecifiedSpec (ConT bstName)] 132 | [] 133 | ( GadtC 134 | [mkSSiName i] 135 | [] 136 | (AppT (ConT protSName) (AppT (PromotedT $ mkSiName i) (VarT sVar))) 137 | ) 138 | else 139 | GadtC [mkSSiName i] [] (AppT (ConT protSName) (PromotedT $ mkSiName i)) 140 | -- generate protocol singleton data type 141 | dataSingletonProt = [DataD [] protSName [KindedTV aVar BndrReq (ConT protName)] Nothing [genSConstr i | i <- stList] []] 142 | 143 | -- generate type family Sing to Singleton protocol 144 | let singSingletonProt = [TySynInstD (TySynEqn Nothing (ConT ''Sing) (ConT protSName))] 145 | 146 | aVar1 <- newName "a" 147 | let branchResultDatas = 148 | [ DataD 149 | [] 150 | dataName 151 | [KindedTV aVar1 BndrReq (ConT protName)] 152 | Nothing 153 | [ GadtC [constrName] (mkArgs args) (AppT (ConT dataName) (tAnyToType (mkName "s") t)) 154 | | (bst, args, t) <- constrs 155 | , let constrName = mkName ("BranchSt_" <> show bst) 156 | ] 157 | [] 158 | | (name, constrs) <- branchResultTypeInfo 159 | , let dataName = mkName name 160 | ] 161 | branchFunTypes <- for branchFunList $ \(r, st, t) -> do 162 | mVar <- newName "m" 163 | pure 164 | ( TySynD 165 | (mkName (st <> "Fun")) 166 | [KindedTV mVar BndrReq (AppT (AppT ArrowT StarT) StarT)] 167 | ( typeListT 168 | [ ConT ''TSC.Peer 169 | , ConT roleName 170 | , ConT protName 171 | , ConT (mkName (show r)) 172 | , VarT mVar 173 | , ConT (mkName st) 174 | , (tAnyToType (mkName "s") t) 175 | ] 176 | ) 177 | ) 178 | let firstTList = case msgT of 179 | Msg (ts, _, _) _ _ _ _ :> _ -> ts 180 | Label (ts, _) _ :> _ -> ts 181 | Branch ts _ _ _ -> ts 182 | Goto (ts, _) _ -> ts 183 | Terminal ts -> ts 184 | 185 | mkTySynDFun name t = 186 | if isTAny t 187 | then do 188 | sVar1 <- newName @Q "s" 189 | pure (TySynD name [KindedTV sVar1 BndrReq (ConT bstName)] (tAnyToType sVar1 t)) 190 | else pure (TySynD name [] (tAnyToType (mkName "s") t)) 191 | 192 | mkAllRoleTySynDFun nameFun ts = 193 | for (zip [minBound @r .. maxBound] ts) $ 194 | \(r, t) -> mkTySynDFun (nameFun r) t 195 | 196 | roleStartSts <- mkAllRoleTySynDFun (\r -> (mkName (show r <> "StartSt"))) firstTList 197 | 198 | allMsgBADecs <- 199 | concat <$> do 200 | for allMsgBATypes $ \(cname, beforeSt, afterSt) -> do 201 | bfs <- mkAllRoleTySynDFun (\r -> mkName (show r <> "Before" <> cname <> "St")) beforeSt 202 | afs <- mkAllRoleTySynDFun (\r -> mkName (show r <> "After" <> cname <> "St")) afterSt 203 | pure (bfs <> afs) 204 | 205 | s1 <- newName "s1" 206 | -- generate instance SingI 207 | let instanceSingI = 208 | [ InstanceD 209 | Nothing 210 | [] 211 | ( AppT 212 | (ConT ''SingI) 213 | ( if i == -1 214 | then PromotedT (mkName "End") 215 | else 216 | if i `Set.member` dnySet 217 | then SigT (AppT (PromotedT (mkSiName i)) (VarT s1)) (ConT protName) 218 | else PromotedT (mkSiName i) 219 | ) 220 | ) 221 | [FunD 'sing [Clause [] (NormalB (ConE (mkName $ "S" <> (if i == -1 then "End" else ("S" <> show i))))) []]] 222 | | i <- stList 223 | ] 224 | 225 | xVar <- newName "x" 226 | -- generate instance SingToInt 227 | let instanceSingToInt = 228 | [ InstanceD 229 | Nothing 230 | [] 231 | (AppT (ConT ''SingToInt) (ConT protName)) 232 | [ FunD 'singToInt [Clause [VarP xVar] (NormalB (AppE (ConE 'I#) (AppE (VarE 'dataToTag#) (VarE xVar)))) []] 233 | ] 234 | ] 235 | 236 | -- make instance type family Done 237 | let instDoneDesc = [TySynInstD (TySynEqn Nothing (ConT (mkName "Done")) (PromotedT (mkName "End")))] 238 | 239 | let mkDataInstanceMsg :: Name -> Protocol (MsgT1 r bst) r bst -> Q [Con] 240 | mkDataInstanceMsg s = \case 241 | Msg ((a, b, c), (from, to), _) constr args _ _ :> prots -> do 242 | let mkTName = 243 | typeListT 244 | [ ConT ''TSC.Msg 245 | , ConT roleName 246 | , ConT protName 247 | , tAnyToType s a 248 | , PromotedT (mkName (show from)) 249 | , tAnyToType s b 250 | , PromotedT (mkName (show to)) 251 | , tAnyToType s c 252 | ] 253 | let val = 254 | let gadtc = 255 | GadtC 256 | [mkName constr] 257 | (mkArgs args) 258 | mkTName 259 | in if any isTAny [a, b, c] 260 | then ForallC [KindedTV s SpecifiedSpec (ConT bstName)] [] gadtc 261 | else gadtc 262 | vals <- mkDataInstanceMsg s prots 263 | pure (val : vals) 264 | Label _ _ :> prots -> mkDataInstanceMsg s prots 265 | Branch _ _ _ ls -> do 266 | ls' <- forM ls $ \(BranchSt _ _ _ prot) -> mkDataInstanceMsg s prot 267 | pure $ concat ls' 268 | _ -> pure [] 269 | 270 | -- make data instance Msg 271 | ssVar <- newName "s" 272 | instMsgDesc <- mkDataInstanceMsg ssVar (msgT1) 273 | fromVar <- newName "from" 274 | sendVar <- newName "send" 275 | sendNewStVar <- newName "sendNewSt" 276 | recvVar <- newName "recv" 277 | receiverNewStVar <- newName "receiverNewSt" 278 | let instanceMsg = 279 | [ InstanceD 280 | Nothing 281 | [] 282 | (AppT (AppT (ConT ''TSC.Protocol) (ConT roleName)) (ConT protName)) 283 | ( instDoneDesc 284 | ++ [ DataInstD 285 | [] 286 | ( Just 287 | [ KindedTV fromVar () (ConT protName) 288 | , KindedTV sendVar () (ConT roleName) 289 | , KindedTV sendNewStVar () (ConT protName) 290 | , KindedTV recvVar () (ConT roleName) 291 | , KindedTV receiverNewStVar () (ConT protName) 292 | ] 293 | ) 294 | ( typeListT 295 | [ ConT ''TSC.Msg 296 | , ConT roleName 297 | , ConT protName 298 | , (SigT (VarT fromVar) (ConT protName)) 299 | , (SigT (VarT sendVar) (ConT roleName)) 300 | , (SigT (VarT sendNewStVar) (ConT protName)) 301 | , (SigT (VarT recvVar) (ConT roleName)) 302 | , (SigT (VarT receiverNewStVar) (ConT protName)) 303 | ] 304 | ) 305 | Nothing 306 | instMsgDesc 307 | [] 308 | ] 309 | ) 310 | ] 311 | 312 | pure $ 313 | dataProt 314 | ++ dataSingletonProt 315 | ++ singSingletonProt 316 | ++ branchResultDatas 317 | ++ branchFunTypes 318 | ++ roleStartSts 319 | ++ allMsgBADecs 320 | ++ instanceSingI 321 | ++ instanceSingToInt 322 | ++ instanceMsg 323 | 324 | protocol' 325 | :: forall r bst 326 | . ( Enum r 327 | , Bounded r 328 | , Show r 329 | , Enum bst 330 | , Bounded bst 331 | , Show bst 332 | , Ord r 333 | ) 334 | => String -> Name -> Name -> Bool -> QuasiQuoter 335 | protocol' protN roleName bstName b = 336 | QuasiQuoter 337 | { quoteExp = const $ fail "No protocol parse for exp" 338 | , quotePat = const $ fail "No protocol parse for pat" 339 | , quoteType = const $ fail "No protocol parser for type" 340 | , quoteDec = parseOrThrow 341 | } 342 | where 343 | parseOrThrow :: String -> Q [Dec] 344 | parseOrThrow st = case runProtocolParser @r @bst st of 345 | Left e -> fail (show e) 346 | Right protCreat -> case pipe protCreat of 347 | Left e -> fail (show e) 348 | Right pipResult -> do 349 | let graphStr = genGraph @r @bst pipResult 350 | when b $ runIO $ do 351 | writeFile (protN <> ".prot") graphStr 352 | putStrLn graphStr 353 | d1 <- roleDecs roleName 354 | d2 <- protDecsAndMsgDecs protN roleName bstName pipResult 355 | pure (d1 ++ d2) 356 | 357 | protocol 358 | :: forall r bst 359 | . ( Enum r 360 | , Bounded r 361 | , Show r 362 | , Enum bst 363 | , Bounded bst 364 | , Show bst 365 | , Ord r 366 | ) 367 | => String -> Name -> Name -> QuasiQuoter 368 | protocol protN roleName bstName = 369 | protocol' @r @bst protN roleName bstName False 370 | -------------------------------------------------------------------------------- /test/Book3/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE MultiParamTypeClasses #-} 7 | {-# LANGUAGE NamedFieldPuns #-} 8 | {-# LANGUAGE PolyKinds #-} 9 | {-# LANGUAGE QualifiedDo #-} 10 | {-# LANGUAGE RankNTypes #-} 11 | {-# LANGUAGE ScopedTypeVariables #-} 12 | {-# LANGUAGE TypeApplications #-} 13 | {-# LANGUAGE TypeFamilies #-} 14 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 15 | 16 | module Book3.Main where 17 | 18 | import Book3.Peer 19 | import Book3.Protocol 20 | import Book3.Type 21 | import Control.Carrier.Lift (runM, sendM) 22 | import Control.Carrier.Random.Gen (runRandom) 23 | import Control.Concurrent.Class.MonadSTM 24 | import Control.Monad 25 | import Control.Monad.Class.MonadFork (MonadFork, forkIO) 26 | import Control.Monad.Class.MonadSay 27 | import Control.Monad.Class.MonadThrow (MonadThrow) 28 | import Control.Monad.Class.MonadTimer (MonadDelay) 29 | import Control.Monad.IOSim 30 | import System.Random (StdGen, split) 31 | import TypedSession.Codec 32 | import TypedSession.Core 33 | import TypedSession.Driver 34 | 35 | mvarsAsChannel 36 | :: (MonadSTM m) 37 | => TMVar m a 38 | -> TMVar m a 39 | -> Channel m a 40 | mvarsAsChannel bufferRead bufferWrite = 41 | Channel{send, recv} 42 | where 43 | send x = atomically (putTMVar bufferWrite x) 44 | recv = atomically (Just <$> takeTMVar bufferRead) 45 | 46 | myTracer :: (MonadSay m) => String -> Tracer BookRole Book m 47 | myTracer st v = say (st <> show v) 48 | 49 | runAll 50 | :: forall n 51 | . ( Monad n 52 | , MonadSTM n 53 | , MonadSay n 54 | , MonadThrow n 55 | , MonadFork n 56 | , MonadDelay n 57 | ) 58 | => StdGen 59 | -> n () 60 | runAll g = do 61 | buyerTMVar <- newEmptyTMVarIO @n @(AnyMsg BookRole Book) 62 | buyer2TMVar <- newEmptyTMVarIO @n @(AnyMsg BookRole Book) 63 | sellerTMVar <- newEmptyTMVarIO @n @(AnyMsg BookRole Book) 64 | 65 | buyerDriver <- 66 | driverSimple 67 | (myTracer "buyer :") 68 | encodeMsg 69 | (Decode decodeMsg) 70 | [ (SomeRole SSeller, mvarsAsChannel buyerTMVar sellerTMVar) 71 | , (SomeRole SBuyer2, mvarsAsChannel buyerTMVar buyer2TMVar) 72 | ] 73 | sendM 74 | 75 | buyer2Driver <- 76 | driverSimple 77 | (myTracer "buyer2 :") 78 | encodeMsg 79 | (Decode decodeMsg) 80 | [(SomeRole SBuyer, mvarsAsChannel buyer2TMVar buyerTMVar)] 81 | sendM 82 | 83 | sellerDriver <- 84 | driverSimple 85 | (myTracer "seller :") 86 | encodeMsg 87 | (Decode decodeMsg) 88 | [(SomeRole SBuyer, mvarsAsChannel sellerTMVar buyerTMVar)] 89 | sendM 90 | 91 | let (g0, g1) = split g 92 | (g2, g3) = split g0 93 | 94 | resultTMVar1 <- newEmptyTMVarIO 95 | resultTMVar2 <- newEmptyTMVarIO 96 | 97 | -- fork seller Peer thread 98 | forkIO $ do 99 | runM $ runRandom g1 $ runPeerWithDriver sellerDriver sellerPeer 100 | atomically $ writeTMVar resultTMVar1 () 101 | 102 | -- fork buyer2 Peer thread 103 | forkIO $ do 104 | runM $ runRandom g2 $ runPeerWithDriver buyer2Driver buyer2Peer 105 | atomically $ writeTMVar resultTMVar2 () 106 | 107 | -- run buyer Peer 108 | void $ runM $ runRandom g3 $ runPeerWithDriver buyerDriver buyerPeer 109 | 110 | -- wait seller, buyer 111 | atomically $ do 112 | takeTMVar resultTMVar1 113 | takeTMVar resultTMVar2 114 | 115 | book3Prop :: StdGen -> Either Failure () 116 | book3Prop v = runSim (runAll v) -------------------------------------------------------------------------------- /test/Book3/Peer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE NamedFieldPuns #-} 9 | {-# LANGUAGE PolyKinds #-} 10 | {-# LANGUAGE QualifiedDo #-} 11 | {-# LANGUAGE RankNTypes #-} 12 | {-# LANGUAGE ScopedTypeVariables #-} 13 | {-# LANGUAGE TypeApplications #-} 14 | {-# LANGUAGE TypeFamilies #-} 15 | {-# LANGUAGE TypeOperators #-} 16 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 17 | 18 | module Book3.Peer where 19 | 20 | import Book3.Protocol 21 | import Book3.Type 22 | import Control.Carrier.Lift 23 | import Control.Carrier.Random.Gen 24 | import Data.IFunctor (At (..), returnAt) 25 | import qualified Data.IFunctor as I 26 | import TypedSession.Core 27 | 28 | budget :: Int 29 | budget = 16 30 | 31 | type Date = Int 32 | 33 | checkPrice 34 | :: (Has Random sig m) 35 | => Int 36 | -> Int 37 | -> EnoughOrNotEnoughFun m 38 | checkPrice _i _h = I.do 39 | At b <- liftm $ uniform @Bool 40 | if b 41 | then liftConstructor BranchSt_Enough 42 | else liftConstructor BranchSt_NotEnough 43 | 44 | choiceOT 45 | :: (Has Random sig m) 46 | => Int 47 | -> OneOrTwoFun m 48 | choiceOT _i = I.do 49 | At b <- liftm $ uniform @Bool 50 | if b 51 | then liftConstructor BranchSt_One 52 | else liftConstructor BranchSt_Two 53 | 54 | buyerPeer 55 | :: (Has Random sig m) 56 | => Peer BookRole Book Buyer m (At (Maybe Date) Done) BuyerStartSt 57 | buyerPeer = I.do 58 | yield (Title "haskell book") 59 | await I.>>= \case 60 | NoBook -> I.do 61 | yield SellerNoBook 62 | returnAt Nothing 63 | Price i -> I.do 64 | choiceOT i I.>>= \case 65 | BranchSt_One -> I.do 66 | yield OneAccept 67 | OneDate d <- await 68 | yield (OneSuccess d) 69 | returnAt $ Just d 70 | BranchSt_Two -> I.do 71 | yield (PriceToBuyer2 (i `div` 2)) 72 | await I.>>= \case 73 | NotSupport1 -> I.do 74 | yield TwoNotBuy 75 | returnAt Nothing 76 | SupportVal h -> I.do 77 | checkPrice 10 h I.>>= \case 78 | BranchSt_Enough -> I.do 79 | yield TwoAccept 80 | TwoDate d <- await 81 | yield (TwoSuccess d) 82 | returnAt (Just d) 83 | BranchSt_NotEnough -> I.do 84 | yield TwoNotBuy1 85 | yield TwoFailed 86 | returnAt Nothing 87 | 88 | choiceB 89 | :: (Has Random sig m) 90 | => Int 91 | -> SupportOrNotSupportFun m 92 | choiceB _i = I.do 93 | At b <- liftm $ uniform @Bool 94 | if b 95 | then liftConstructor BranchSt_Support 96 | else liftConstructor BranchSt_NotSupport 97 | 98 | buyer2Peer 99 | :: (Has Random sig m) 100 | => Peer BookRole Book Buyer2 m (At (Maybe Date) Done) (Buyer2StartSt s) 101 | buyer2Peer = I.do 102 | await I.>>= \case 103 | SellerNoBook -> returnAt Nothing 104 | (OneSuccess d) -> returnAt (Just d) 105 | (PriceToBuyer2 i) -> I.do 106 | choiceB i I.>>= \case 107 | BranchSt_NotSupport -> I.do 108 | yield NotSupport1 109 | returnAt Nothing 110 | BranchSt_Support -> I.do 111 | yield (SupportVal (i `div` 2)) 112 | await I.>>= \case 113 | TwoSuccess d -> returnAt $ Just d 114 | TwoFailed -> returnAt Nothing 115 | 116 | findBook 117 | :: (Has Random sig m) 118 | => String 119 | -> FindBookResultFun m 120 | findBook _st = I.do 121 | At b <- liftm $ uniform @Bool 122 | if b 123 | then liftConstructor BranchSt_Found 124 | else liftConstructor BranchSt_NotFound 125 | 126 | sellerPeer 127 | :: (Has Random sig m) 128 | => Peer BookRole Book Seller m (At () Done) SellerStartSt 129 | sellerPeer = I.do 130 | (Title st) <- await 131 | findBook st I.>>= \case 132 | BranchSt_NotFound -> yield NoBook 133 | BranchSt_Found -> I.do 134 | yield (Price 30) 135 | await I.>>= \case 136 | OneAccept -> yield (OneDate 100) 137 | TwoNotBuy -> returnAt () 138 | TwoAccept -> yield (TwoDate 100) 139 | TwoNotBuy1 -> returnAt () 140 | 141 | -- data AnyPeer role' ps m where 142 | -- AnyPeer :: Peer role' ps r m (At () (Done r)) st -> AnyPeer role' ps m 143 | 144 | -- runAnyPeers 145 | -- :: forall n role' ps sig m 146 | -- . ( SingToInt role' 147 | -- , Has (State (IntMap (AnyPeer role' ps n))) sig m 148 | -- ) 149 | -- => (forall a. n a -> m a) -> m () 150 | -- runAnyPeers liftFun = do 151 | -- im <- get @(IntMap (AnyPeer role' ps n)) 152 | -- case IntMap.keys im of 153 | -- [] -> pure () 154 | -- keys -> do 155 | -- forM_ keys $ \key -> do 156 | -- gets @(IntMap (AnyPeer role' ps n)) (IntMap.lookup key) >>= \case 157 | -- Nothing -> error "np" 158 | -- Just (AnyPeer peer) -> case peer of 159 | -- IReturn (At ()) -> do 160 | -- modify @(IntMap (AnyPeer role' ps n)) (IntMap.delete key) 161 | -- LiftM fm -> do 162 | -- np <- liftFun fm 163 | -- modify @(IntMap (AnyPeer role' ps n)) (IntMap.insert key (AnyPeer np)) 164 | -- Yield (msg :: Msg role' ps st send sps recv rps) cont -> do 165 | -- let recvKey = (singToInt $ sing @recv) 166 | -- gets @(IntMap (AnyPeer role' ps n)) (IntMap.lookup recvKey) >>= \case 167 | -- Nothing -> error "np" 168 | -- Just (AnyPeer recvPeer) -> case recvPeer of 169 | -- Await scont -> do 170 | -- let nS = scont (unsafeCoerce msg) 171 | -- modify @(IntMap (AnyPeer role' ps n)) (IntMap.insert recvKey (AnyPeer nS)) 172 | -- _ -> error "np" 173 | -- modify @(IntMap (AnyPeer role' ps n)) (IntMap.insert key (AnyPeer cont)) 174 | -- Await{} -> pure () 175 | -- runAnyPeers @n @role' @ps liftFun 176 | 177 | -- runAP = do 178 | -- i <- randomIO @Int 179 | -- runRandom (mkStdGen i) 180 | -- $ runM @(RandomC StdGen IO) 181 | -- $ runState 182 | -- ( IntMap.fromList 183 | -- [ (singToInt SBuyer, AnyPeer (buyerPeer @_ @(RandomC StdGen IO) I.>> returnAt ())) 184 | -- , (singToInt SBuyer2, AnyPeer (buyer2Peer @_ @(RandomC StdGen IO) I.>> returnAt ())) 185 | -- , (singToInt SSeller, AnyPeer (sellerPeer @_ @(RandomC StdGen IO) I.>> returnAt ())) 186 | -- ] 187 | -- ) 188 | -- $ (runAnyPeers @(RandomC StdGen IO) @BookRole @Book sendM) 189 | -------------------------------------------------------------------------------- /test/Book3/Protocol.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | {-# LANGUAGE GADTs #-} 4 | {-# LANGUAGE KindSignatures #-} 5 | {-# LANGUAGE LambdaCase #-} 6 | {-# LANGUAGE MagicHash #-} 7 | {-# LANGUAGE MultiParamTypeClasses #-} 8 | {-# LANGUAGE PolyKinds #-} 9 | {-# LANGUAGE QuasiQuotes #-} 10 | {-# LANGUAGE TemplateHaskell #-} 11 | {-# LANGUAGE TypeApplications #-} 12 | {-# LANGUAGE TypeFamilies #-} 13 | {-# OPTIONS_GHC -Wno-orphans #-} 14 | {-# OPTIONS_GHC -Wno-unused-do-bind #-} 15 | 16 | module Book3.Protocol where 17 | 18 | import Book3.Type 19 | 20 | import TypedSession.Codec 21 | import TypedSession.Core 22 | 23 | 24 | [bookProtocol| 25 | Label 0 26 | Msg Title [String] Buyer Seller 27 | Branch Seller FindBookResult { 28 | BranchSt NotFound [] 29 | Msg NoBook [] Seller Buyer 30 | Msg SellerNoBook [] Buyer Buyer2 31 | Terminal 32 | BranchSt Found [] 33 | Msg Price [Int] Seller Buyer 34 | Branch Buyer OneOrTwo { 35 | BranchSt One [] 36 | Msg OneAccept [] Buyer Seller 37 | Msg OneDate [Int] Seller Buyer 38 | Msg OneSuccess [Int] Buyer Buyer2 39 | Terminal 40 | BranchSt Two [] 41 | Msg PriceToBuyer2 [Int] Buyer Buyer2 42 | Branch Buyer2 SupportOrNotSupport { 43 | BranchSt NotSupport [] 44 | Msg NotSupport1 [] Buyer2 Buyer 45 | Msg TwoNotBuy [] Buyer Seller 46 | Terminal 47 | BranchSt Support [] 48 | Msg SupportVal [Int] Buyer2 Buyer 49 | Branch Buyer EnoughOrNotEnough { 50 | BranchSt Enough [] 51 | Msg TwoAccept [] Buyer Seller 52 | Msg TwoDate [Int] Seller Buyer 53 | Msg TwoSuccess [Int] Buyer Buyer2 54 | Terminal 55 | BranchSt NotEnough [] 56 | Msg TwoNotBuy1 [] Buyer Seller 57 | Msg TwoFailed [] Buyer Buyer2 58 | Terminal 59 | } 60 | } 61 | } 62 | } 63 | 64 | |] 65 | 66 | encodeMsg :: Encode BookRole Book (AnyMsg BookRole Book) 67 | encodeMsg = Encode $ \x -> case x of 68 | Title{} -> AnyMsg x 69 | NoBook{} -> AnyMsg x 70 | SellerNoBook{} -> AnyMsg x 71 | Price{} -> AnyMsg x 72 | OneAccept{} -> AnyMsg x 73 | OneDate{} -> AnyMsg x 74 | OneSuccess{} -> AnyMsg x 75 | PriceToBuyer2{} -> AnyMsg x 76 | NotSupport1{} -> AnyMsg x 77 | TwoNotBuy{} -> AnyMsg x 78 | SupportVal{} -> AnyMsg x 79 | TwoAccept{} -> AnyMsg x 80 | TwoDate{} -> AnyMsg x 81 | TwoSuccess{} -> AnyMsg x 82 | TwoNotBuy1{} -> AnyMsg x 83 | TwoFailed{} -> AnyMsg x 84 | 85 | decodeMsg 86 | :: DecodeStep 87 | (AnyMsg BookRole Book) 88 | CodecFailure 89 | (AnyMsg BookRole Book) 90 | decodeMsg = 91 | DecodePartial $ \case 92 | Nothing -> DecodeFail (CodecFailure "expected more data") 93 | Just anyMsg -> DecodeDone anyMsg Nothing 94 | 95 | instance Show (AnyMsg BookRole Book) where 96 | show (AnyMsg msg) = case msg of 97 | Title st -> "Title " <> show st 98 | NoBook -> "NoBook" 99 | SellerNoBook -> "SellerNoBook" 100 | Price i -> "Price " <> show i 101 | OneAccept -> "OneAccept" 102 | OneDate d -> "OneDate " <> show d 103 | OneSuccess d -> "OneSuccess" <> show d 104 | PriceToBuyer2 i -> "PriceToBuyer2 " <> show i 105 | NotSupport1 -> "NotSupport1" 106 | TwoNotBuy -> "TwoNotBuy" 107 | SupportVal v -> "SupportVal " <> show v 108 | TwoAccept -> "TwoAccept" 109 | TwoDate d -> "TwoDate " <> show d 110 | TwoSuccess d -> "TwoSuccess " <> show d 111 | TwoNotBuy1 -> "TwoNotBuy1" 112 | TwoFailed -> "TwoFailed" 113 | -------------------------------------------------------------------------------- /test/Book3/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE FlexibleInstances #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | {-# LANGUAGE MagicHash #-} 8 | {-# LANGUAGE MultiParamTypeClasses #-} 9 | {-# LANGUAGE PolyKinds #-} 10 | {-# LANGUAGE QualifiedDo #-} 11 | {-# LANGUAGE RankNTypes #-} 12 | {-# LANGUAGE ScopedTypeVariables #-} 13 | {-# LANGUAGE TemplateHaskell #-} 14 | {-# LANGUAGE TypeApplications #-} 15 | {-# LANGUAGE TypeFamilies #-} 16 | 17 | module Book3.Type where 18 | 19 | import Language.Haskell.TH.Quote (QuasiQuoter) 20 | import TypedSession.TH (protocol) 21 | 22 | data BookRole = Buyer | Seller | Buyer2 23 | deriving (Show, Eq, Ord, Enum, Bounded) 24 | 25 | data BookBranchSt 26 | = NotFound 27 | | Found 28 | | One 29 | | Two 30 | | Support 31 | | NotSupport 32 | | Enough 33 | | NotEnough 34 | deriving (Show, Eq, Ord, Enum, Bounded) 35 | 36 | bookProtocol :: QuasiQuoter 37 | bookProtocol = 38 | protocol 39 | @BookRole 40 | @BookBranchSt 41 | "Book" 42 | ''BookRole 43 | ''BookBranchSt 44 | -------------------------------------------------------------------------------- /test/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | module Main (main) where 3 | 4 | import Book3.Main 5 | import Control.Monad 6 | import System.Random 7 | import Control.Exception 8 | 9 | main :: IO () 10 | main = do 11 | replicateM_ 100 $ do 12 | g <- newStdGen 13 | case book3Prop g of 14 | Left e -> throwIO e 15 | Right _ -> pure () 16 | -------------------------------------------------------------------------------- /typed-session.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | -- The cabal-version field refers to the version of the .cabal specification, 3 | -- and can be different from the cabal-install (the tool) version and the 4 | -- Cabal (the library) version you are using. As such, the Cabal (the library) 5 | -- version used must be equal or greater than the version stated in this field. 6 | -- Starting from the specification version 2.2, the cabal-version field must be 7 | -- the first thing in the cabal file. 8 | 9 | -- Initial package description 'typed-session' generated by 10 | -- 'cabal init'. For further documentation, see: 11 | -- http://haskell.org/cabal/users-guide/ 12 | -- 13 | -- The name of the package. 14 | name: typed-session 15 | 16 | -- The package version. 17 | -- See the Haskell package versioning policy (PVP) for standards 18 | -- guiding when and how versions should be incremented. 19 | -- https://pvp.haskell.org 20 | -- PVP summary: +-+------- breaking API changes 21 | -- | | +----- non-breaking API additions 22 | -- | | | +--- code changes with no API change 23 | version: 0.3.0.1 24 | 25 | -- A short (one-line) description of the package. 26 | synopsis: typed session framework 27 | 28 | -- A longer description of the package. 29 | description: 30 | Typed session are used to ensure desirable properties in concurrent and distributed systems, 31 | i.e. absence of communication errors or deadlocks, and protocol conformance. 32 | 33 | -- The license under which the package is released. 34 | license: MIT 35 | 36 | -- The file containing the license text. 37 | license-file: LICENSE 38 | 39 | -- The package author(s). 40 | author: sdzx-1 41 | 42 | -- An email address to which users can send suggestions, bug reports, and patches. 43 | maintainer: shangdizhixia1993@163.com 44 | 45 | category: Control, Network 46 | -- A copyright notice. 47 | -- copyright: 48 | build-type: Simple 49 | 50 | -- Extra doc files to be distributed with the package, such as a CHANGELOG or a README. 51 | extra-doc-files: CHANGELOG.md 52 | 53 | -- Extra source files to be distributed with the package, such as examples, or a tutorial module. 54 | -- extra-source-files: 55 | extra-doc-files: data/*.png 56 | 57 | common warnings 58 | ghc-options: -Wall 59 | 60 | library 61 | -- Import common warning flags. 62 | import: warnings 63 | 64 | -- Modules exported by the library. 65 | exposed-modules: Data.IFunctor 66 | , TypedSession.Core 67 | , TypedSession.Codec 68 | , TypedSession.Driver 69 | , TypedSession.TH 70 | 71 | -- Modules included in this library but not exported. 72 | -- other-modules: 73 | 74 | -- LANGUAGE extensions used by modules in this package. 75 | -- other-extensions: 76 | 77 | -- Other library packages from which modules are imported. 78 | build-depends: base >= 4.20.0 && < 4.21 79 | , containers >= 0.7 && < 0.8 80 | , io-classes >= 1.7.0 && < 1.8 81 | , template-haskell >= 2.22.0 && < 2.23 82 | , typed-session-state-algorithm >= 0.5.0 && < 0.6 83 | 84 | -- Directories containing source files. 85 | hs-source-dirs: src 86 | 87 | -- Base language which the package is written in. 88 | default-language: Haskell2010 89 | 90 | test-suite typed-session-test 91 | -- Import common warning flags. 92 | import: warnings 93 | 94 | -- Base language which the package is written in. 95 | default-language: Haskell2010 96 | 97 | -- Modules included in this executable, other than Main. 98 | other-modules: Book3.Type 99 | , Book3.Protocol 100 | , Book3.Peer 101 | , Book3.Main 102 | 103 | -- LANGUAGE extensions used by modules in this package. 104 | -- other-extensions: 105 | 106 | -- The interface type and version of the test suite. 107 | type: exitcode-stdio-1.0 108 | 109 | -- Directories containing source files. 110 | hs-source-dirs: test 111 | 112 | -- The entrypoint to the test suite. 113 | main-is: Main.hs 114 | 115 | -- Test dependencies. 116 | build-depends: 117 | base >=4.20.0.0, 118 | typed-session, 119 | containers, 120 | io-classes, 121 | io-sim, 122 | random, 123 | fused-effects, 124 | fused-effects-random, 125 | template-haskell 126 | ghc-options: -Werror=inaccessible-code -threaded 127 | 128 | source-repository head 129 | type: git 130 | location: https://github.com/sdzx-1/typed-session 131 | --------------------------------------------------------------------------------