Skip to content

Commit

Permalink
feat(runtime): more flexibility around state
Browse files Browse the repository at this point in the history
  • Loading branch information
symbiont-stevan-andjelkovic committed May 18, 2021
1 parent a145d35 commit 7236107
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/runtime-prototype/src/StuntDouble.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module StuntDouble (module X) where
import StuntDouble.Actor as X
import StuntDouble.Actor.State as X
import StuntDouble.EventLoop as X
import StuntDouble.Datatype as X
import StuntDouble.Message as X
import StuntDouble.EventLoop.Event as X
import StuntDouble.EventLoop.Transport as X
Expand Down
77 changes: 72 additions & 5 deletions src/runtime-prototype/src/StuntDouble/Actor.hs
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE StandaloneDeriving #-}

module StuntDouble.Actor where

import Control.Concurrent.Async
import Data.Text (Text)
import Data.Time

import StuntDouble.Message
import StuntDouble.Reference
import StuntDouble.FreeMonad
import StuntDouble.Actor.State
import StuntDouble.Datatype

------------------------------------------------------------------------

-- XXX: use newtype and avoid flexible instances for monad fail...
type Actor = Free ActorF (Cont Message)
instance MonadFail (Free ActorF) where
fail = error

data Cont a
= Now a
Expand All @@ -29,7 +36,7 @@ data Cont a
-- | Atleast Int
-- | ...

data IOResult = Unit | String String
data IOResult = IOUnit | String String
deriving (Eq, Ord, Show)

data ActorF x
Expand Down Expand Up @@ -61,8 +68,68 @@ unsafeAwait a = Free (UnsafeAwait a return)
asyncIO :: IO IOResult -> Free ActorF (Async IOResult)
asyncIO m = Free (AsyncIO m return)

get :: Free ActorF State
get = Free (Get return)
getState :: Free ActorF State
getState = Free (Get return)

put :: State -> Free ActorF ()
put state' = Free (Put state' return)
putState :: State -> Free ActorF ()
putState state' = Free (Put state' return)

------------------------------------------------------------------------

class Convertible a where
convertFrom :: a -> Datatype
convertTo :: Datatype -> a

instance Convertible Integer where
convertFrom = Integer
convertTo (Integer i) = i

instance Convertible Text where
convertFrom = Text
convertTo (Text t) = t

instance Convertible UTCTime where
convertFrom = Timestamp
convertTo (Timestamp t) = t

instance (Convertible a, Convertible b) => Convertible (a, b) where
convertFrom (x, y) = Pair (convertFrom x) (convertFrom y)
convertTo (Pair x y) = (convertTo x, convertTo y)

instance Convertible a => Convertible (Maybe a) where
convertFrom Nothing = None
convertFrom (Just x) = Some (convertFrom x)

convertTo None = Nothing
convertTo (Some x) = Just (convertTo x)

instance Convertible Datatype where
convertFrom = id
convertTo = id

(^.) :: Convertible a => Text -> (Datatype -> Datatype) -> Free ActorF a
k ^. a = do
s <- getState
return (convertTo (a (getField k s)))

(.=) :: Convertible a => Text -> a -> Free ActorF ()
k .= v = do
s <- getState
putState (setField k (convertFrom v) s)

(%=) :: (Datatype -> Datatype) -> Text -> Free ActorF ()
f %= k = do
s <- getState
putState (modifyField k f s)

op2 :: (Convertible a, Convertible b)
=> (Datatype -> Datatype -> Datatype -> Datatype) -> a -> b -> (Datatype -> Datatype)
op2 f x y = f (convertFrom x) (convertFrom y)

get :: Convertible a => Text -> Free ActorF a
get k = do
s <- getState
return (convertTo (getField k s))

genArrivalTime :: UTCTime -> Integer -> Free ActorF UTCTime
genArrivalTime = undefined
5 changes: 4 additions & 1 deletion src/runtime-prototype/src/StuntDouble/Actor/State.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import StuntDouble.Datatype

------------------------------------------------------------------------

newtype State = State { getState :: HashMap Text Datatype }
newtype State = State { getHashMap :: HashMap Text Datatype }
deriving Show

initState :: State
Expand All @@ -26,5 +26,8 @@ getField :: Text -> State -> Datatype
getField k (State hm) = hm HashMap.! k
getField _k _otherwise = error "getField: impossible, invalid state."

modifyField :: Text -> (Datatype -> Datatype) -> State -> State
modifyField k f = withHashMap (HashMap.adjust f k)

add :: Text -> Integer -> State -> State
add k v = withHashMap (HashMap.alter (Just . maybe (Integer v) (plus (Integer v))) k)
20 changes: 16 additions & 4 deletions src/runtime-prototype/src/StuntDouble/Datatype.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module StuntDouble.Datatype where

import Data.Time
import Data.Heap (Heap)
import qualified Data.Heap as Heap
import Data.Map (Map)
import Data.Text

Expand All @@ -12,17 +14,27 @@ data Datatype
| Double Double
| Bool Bool
| Text Text
| Enum [Text]
| Enum Text
| Pair Datatype Datatype
| Inl Datatype
| Inr Datatype
-- | Timestamp UTCTime
| Timestamp UTCTime
| None
| Some Datatype
| Map (Map Datatype Datatype)
| List [Datatype]
| Heap (Heap Datatype)
deriving Show
| Heap (Heap (Heap.Entry Datatype Datatype))
deriving (Eq, Ord, Show)

------------------------------------------------------------------------

plus :: Datatype -> Datatype -> Datatype
plus (Integer i) (Integer j) = Integer (i + j)

pop :: Datatype -> Datatype
pop (Heap h) = case Heap.uncons h of
Nothing -> None
Just (x, h') -> Some (Pair (Heap.payload x) (Heap h'))

push :: Datatype -> Datatype -> Datatype -> Datatype
push p x (Heap h) = Heap (Heap.insert (Heap.Entry p x) h)
6 changes: 4 additions & 2 deletions src/runtime-prototype/stunt-double.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,16 @@ library
base >=4.14 && <4.15
, containers
, filepath
, text
, time
, mtl
, unix
, stm

build-depends:
aeson
, async
, random
, unix
, text
, unordered-containers
, heaps

Expand All @@ -82,6 +83,7 @@ test-suite test
, stunt-double
, tasty
, tasty-hunit
, text

other-modules:
StuntDouble.EventLoop.TransportTest
Expand Down
6 changes: 3 additions & 3 deletions src/runtime-prototype/test/StuntDouble/EventLoopTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ unit_asyncIO = do

statefulActor :: Message -> Actor
statefulActor (Message intStr) = do
s <- get
s <- getState
let int :: Integer
int = read intStr
s' :: State
s' = add "x" int s
put s'
return (Now (Message (show (getState s'))))
putState s'
return (Now (Message (show (getHashMap s'))))

unit_state :: Assertion
unit_state = do
Expand Down
33 changes: 23 additions & 10 deletions src/runtime-prototype/test/StuntDouble/SchedulerTest.hs
Original file line number Diff line number Diff line change
@@ -1,41 +1,54 @@
{-# LANGUAGE OverloadedStrings #-}

module StuntDouble.SchedulerTest where

import Data.Text (Text)
import Control.Concurrent.Async
import Test.HUnit

import StuntDouble

------------------------------------------------------------------------

executorPort :: Int
executorPort = 3004

fakeExecutor :: IO ()
fakeExecutor = do
let port = 3004
t <- httpTransport port
t <- httpTransport executorPort
e <- transportReceive t
envelopeMessage e @?= envelopeMessage e -- XXX: check if cmd is of the right shape
let resp = replyEnvelope e (Message "XXX: needs the right shape")
transportSend t resp

fakeScheduler :: RemoteRef -> Message -> Actor
fakeScheduler executor (Message "step") = do
cmd <- undefined -- popHeap
a <- remoteCall executor cmd
resp <- unsafeAwait (Left a)
(cmd, heap') <- "heap" ^. pop
"heap" .= (heap' :: Datatype)
a <- remoteCall executor (Message (prettyCommand cmd))
Left resp <- unsafeAwait (Left a)
-- assert resp -- XXX: check if of the right shape
now <- undefined -- get "time" from state
seed <- undefined -- get "seed"
arrivalTime <- undefined -- genArrivalTime now seed
-- pushHeap arrivalTime resp
now <- get "time"
seed <- get "seed"
arrivalTime <- genArrivalTime now seed
op2 push arrivalTime (parseCommand resp) %= "heap"
return (Now (Message "stepped"))

where
parseCommand :: Message -> Datatype
parseCommand (Message m) = Pair (Text "command") (Map undefined {- XXX: args -})

prettyCommand :: Text -> String
prettyCommand _ = "XXX: command"

unit_scheduler :: Assertion
unit_scheduler = do
aExecutor <- async fakeExecutor
elog <- emptyEventLog
let ev = EventLoopName "scheduler"
el <- makeEventLoop "/tmp" ev elog

let executorRef = RemoteRef "http://localhost:3004" 0
let executorRef = RemoteRef ("http://localhost:" ++ show executorPort) 0
lref <- spawn el (fakeScheduler executorRef)
a <- send el (localToRemoteRef ev lref) (Message "step")
reply <- wait a
Expand Down

0 comments on commit 7236107

Please sign in to comment.