MultiChor-1.0.0.0: examples/RingLeader.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}
{-
# This example was carried over from earlier work, likely HasChor. It doesn't yet have a unit test attached to it.
# Ring leader election
Experinmental implementaion of ring leader election.
-}
module RingLeader where
import Choreography
import Choreography.Network.Http
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import GHC.TypeLits (KnownSymbol)
import System.Environment
-- an edge of the ring is represented as a tuple of two locaitons l and l' where
-- l is on the left of l'
data Edge g
= forall l l'.
(KnownSymbol l, KnownSymbol l') =>
Edge (Member l g) (Member l' g)
-- a ring is a sequence of edges
type Ring g = [Edge g]
type Label = Int
ringLeader :: forall g. (KnownSymbols g) => Ring g -> Choreo g (StateT Label IO) () -- g for graph
ringLeader r = loop r
where
loop :: Ring g -> Choreo g (StateT Label IO) ()
loop [] = loop r -- not very safe!
loop (x : xs) = do
finished <- talkToRight x
if finished
then return ()
else loop xs
talkToRight :: Edge g -> Choreo g (StateT Label IO) Bool
talkToRight (Edge left right) = do
ll <- left `_locally` get
labelLeft <- (left, ll) ~> right @@ nobody
labelRight <- right `_locally` get
finished <-
right `locally` \un ->
return $ un singleton labelLeft == un singleton labelRight
broadcast (right, finished) >>= \case
True -> do
right `_locally_` lift (putStrLn "I'm the leader")
return True
False -> do
right `locally_` \un -> put (max (un singleton labelLeft) (un singleton labelRight))
return False
$(mkLoc "nodeA")
$(mkLoc "nodeB")
$(mkLoc "nodeC")
$(mkLoc "nodeD")
type Participants = ["nodeA", "nodeB", "nodeC", "nodeD"]
ring :: Ring Participants
ring =
[ Edge nodeA nodeB,
Edge nodeB nodeC,
Edge nodeC nodeD,
Edge nodeD nodeA
]
main :: IO ()
main = do
[loc] <- getArgs
putStrLn "Please input a label:"
label <- read <$> getLine
_ <- runStateT (runChoreography config (ringLeader ring) loc) label
return ()
where
config =
mkHttpConfig
[ ("nodeA", ("localhost", 4242)),
("nodeB", ("localhost", 4343)),
("nodeC", ("localhost", 4444)),
("nodeD", ("localhost", 4545))
]