Last active
March 29, 2019 14:59
-
-
Save useronym/0f736274f0eba79bec97960299a299f2 to your computer and use it in GitHub Desktop.
Proposal for a Haskell EDSL for specification of processes with protocols
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Types: Protocol monad: `Protocol a r` describes a computation which behaves | |
-- according the specification `a` before finally returning the value r. | |
-- Specification: can describe reading/writing atomic types, composition, and | |
-- branches, and infinite loops. | |
data Atomic = String | Int | … -- can this be rather a constraint on, say, Serializable? | |
-- can we send/receive functions? (we really want to) | |
data Spec = | |
| Read Atomic | |
| Write Atomic | |
| Comp Spec Spec -- composition | |
| Branch [Spec] -- branching: behave as one of these `Spec`s, depending on some test | |
| Loop Spec -- keep repeating this `Spec` ad infinum | |
-- Examples: | |
-- (1) | |
ping :: Protocol (Read String `Comp` Write String) String | |
ping = do | |
receiveString "ping" (reply "pong") -- Must receive this exact string, or fail, syntactic sugar for case' (see below) | |
-- (2) | |
curriedPlus :: Protocol (Read Int `Comp` Read Int `Comp` Write Int) Int | |
curriedPlus = do | |
a <- receive -- These must be inferred to be numbers | |
b <- receive -- Non-numbers must throw **run-time** exception | |
reply $ a + b | |
-- (3) | |
branched :: Protocol (Read String `Comp` | |
Branch [Read String `Comp` Write String | |
,Read Int `Comp` Read Int `Comp` Write Int]) | |
Either Int String -- something like that | |
branched = do | |
sel <- receive | |
case' sel [ | |
("ping", ping) | |
, ("plus", curriedPlus) | |
] -- other selectors must throw error | |
facServ :: Protocol (Loop (Read Int `Comp` Write Int)) () -- or Void? | |
facServ = loop $ do | |
n <- receive | |
reply $ factorial n | |
-- The Spec types should be inferrable by GHC. | |
-- Usage examples (using pseudo-interpreter-syntax): | |
call ping ["ping"] | |
> ["pong"] | |
call ping ["Hello!"] | |
> Protocol error: in branch alternative: no branches matched selector "Hello!", expected one of: "ping", before: replying with string | |
call curriedPlus [1] | |
> Protocol error: expected number, before: replying with number | |
call curriedPlus [] | |
> Protocol error: expected number, before: expecting number, replying with number | |
call curriedPlus [1, 2] | |
> [3] | |
call branched ["help?"] | |
> Protocol error: in branch alternative: no branches matched selector "help?", expected one of: "ping", "plus", before: executing one of branch altrnatives: [… description of branches …] | |
call branched ["plus", 1] | |
> Protocol error: expected number, before: replying with number -- probably also say we got to a branch & describe it | |
call branched ["plus", 1, 2] | |
> [3] | |
-- The above are serial, in reality it should be possible to execute a protocol concurrently with some other protocol, e.g. given: | |
concurrentTest :: Protocol (Read String `Comp` Write String `Comp` Read String `Comp` Write String) () | |
concurrentTest = expectString "Hello" $ do | |
reply "And hello to you, friend!" | |
msg <- expect | |
reply $ "You sent me " ++ msg | |
-- we can write in some other protocol | |
testIt :: Protocol (Write String `Comp` Read String `Comp` Write String `Comp` Read String) String | |
testIt = do | |
reply "Hello" | |
_ <- expect | |
reply "I'm sending you a message" | |
expect | |
-- This raises the issue of what to do in process A if process B returns an | |
-- error: firstly, there needs to be a way to "catch" this error and not crash | |
-- A (though this should be the default); secondly, if B failed and we wish to | |
-- fail in A as well, there needs to be a nice error message from A ("process | |
-- B failed unexpectedly, returning: [… error from B …], while executing [… | |
-- context of where we were in a …] | |
-- Still another consideration is that we also need to be able to read / write | |
-- from multiple processes in one process (e.g. a process that collects | |
-- results from worker processes). | |
-- Not sure how to handle this on the type level? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment