on master help sign in
checkpoint before i delete most of this
commit8e47f923de763a2e4f3a18acaaf1b72ea4f8a124 authorMark Wotton <mwotton@gmail.com> dateSat 14 Nov 2020, 14:25 (6 years ago) parent6fcaa26
7 files changed+118−87
package.yaml+4−2 roboservant.cabal+4−3 src/Roboservant/StateMachine.hs+87−61 src/Roboservant/Types.hs+11−9 src/Roboservant/Types/ReifiedApi.hs+7−7 stack.yaml+1−1 stack.yaml.lock+4−4
package.yaml+4−2
@@ -26,8 +26,10 @@ dependencies:
2626- servant-server # >= 0.17
2727- string-conversions
2828
29−ghc-options: -Wall
30−
29+ghc-options:
30+- -Wall
31+- -fwrite-ide-info
32+- -hiedir=.hie
3133library:
3234 source-dirs: src
3335
roboservant.cabal+4−3
@@ -4,7 +4,7 @@ cabal-version: 1.12
44--
55-- see: https://github.com/sol/hpack
66--
7−-- hash: 4f60e8b535f448b3d09b5f49b6851d27412b133b35f4381078d9693228b977cb
7+-- hash: eec4253f2196a058b05c08988d5c544f7238982abba63f6b0112014f043771b2
88
99name: roboservant
1010version: 0.1.0.2
@@ -30,6 +30,7 @@ source-repository head
3030library
3131 exposed-modules:
3232 Roboservant
33+ Roboservant.Direct
3334 Roboservant.Hedgehog
3435 Roboservant.StateMachine
3536 Roboservant.Types
@@ -40,7 +41,7 @@ library
4041 Paths_roboservant
4142 hs-source-dirs:
4243 src
43− ghc-options: -Wall
44+ ghc-options: -Wall -fwrite-ide-info -hiedir=.hie
4445 build-depends:
4546 base >=4.7 && <5
4647 , bytestring
@@ -65,7 +66,7 @@ test-suite roboservant-test
6566 Paths_roboservant
6667 hs-source-dirs:
6768 test
68− ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
69+ ghc-options: -Wall -fwrite-ide-info -hiedir=.hie -threaded -rtsopts -with-rtsopts=-N
6970 build-depends:
7071 aeson
7172 , base >=4.7 && <5
src/Roboservant/StateMachine.hs+87−61
@@ -18,75 +18,94 @@
1818
1919module Roboservant.StateMachine
2020 ( prop_sequential,
21− prop_concurrent,
2221 )
2322where
2423
24+-- prop_concurrent,
25+
2526import Control.Monad (guard)
2627import Control.Monad.IO.Class (MonadIO, liftIO)
2728import Data.Dynamic (Dynamic, dynApply, dynTypeRep, fromDynamic)
28−import Data.IORef (IORef, newIORef)
2929import qualified Data.List.NonEmpty as NEL
3030import qualified Data.Map.Strict as Map
3131import Data.Maybe (mapMaybe)
3232import Data.Typeable (TypeRep)
33−import Debug.Trace
34−import GHC.IORef (readIORef)
3533import Hedgehog
34+ ( Callback (Update),
35+ Command (Command),
36+ Concrete,
37+ MonadGen,
38+ Opaque (Opaque),
39+ PropertyT,
40+ Symbolic,
41+ Var,
42+ executeSequential,
43+ forAll,
44+ opaque,
45+ )
3646import qualified Hedgehog.Gen as Gen
3747import qualified Hedgehog.Range as Range
38−import Roboservant.Hedgehog
48+import Roboservant.Hedgehog (elementOrFail)
3949import Roboservant.Types
40−import Servant
50+ ( ApiOffset (..),
51+ FlattenServer (..),
52+ Op (..),
53+ ReifiedApi,
54+ State (..),
55+ ToReifiedApi (..),
56+ emptyState,
57+ )
58+import Servant (Endpoints, Proxy (Proxy), Server, ServerError)
4159import Type.Reflection (SomeTypeRep)
4260
4361callEndpoint :: (MonadGen n, MonadIO m) => ReifiedApi -> Command n m State
4462callEndpoint staticRoutes =
4563 let gen :: MonadGen n => State Symbolic -> Maybe (n (Op Symbolic))
4664 gen State {..} = case chewable of
47− -- Chewable values still need to be broken down.
48− (c : _) -> Just $ pure $ Chewable c
65+ (_ : _) -> Just $ do
66+ (offset, v) <- Gen.element (zip [0 ..] chewable)
67+ pure $ Chewable offset _ _
4968 _ -> do
5069 guard $ not $ null options
5170 pure $ uncurry Op <$> chooseOne options
5271 where
5372 chooseOne ::
5473 MonadGen n =>
55− [ ( ApiOffset,
56− [[Var (Opaque (IORef Dynamic)) Symbolic]]
74+ [ ( a,
75+ [[b]]
5776 )
5877 ] ->
5978 n
60− ( ApiOffset,
61− [Var (Opaque (IORef Dynamic)) Symbolic]
79+ ( a,
80+ [b]
6281 )
6382 chooseOne opts = do
6483 (offset, args) <- elementOrFail opts
6584 (offset,) <$> mapM elementOrFail args
66− options :: [(ApiOffset, [[Var (Opaque (IORef Dynamic)) Symbolic]])]
85+ options :: [(ApiOffset, [[Var (Opaque Dynamic) Symbolic]])]
6786 options =
6887 mapMaybe
69− ( \(offset, (argreps, _retType, _dynCall)) -> (offset,) <$> do
88+ ( \(offset, (argreps, _dynCall)) -> (offset,) <$> do
7089 mapM fillableWith argreps
7190 )
7291 staticRoutes
73− fillableWith :: TypeRep -> Maybe [Var (Opaque (IORef Dynamic)) Symbolic]
92+ fillableWith :: TypeRep -> Maybe [Var (Opaque Dynamic) Symbolic]
7493 fillableWith tr = NEL.toList <$> Map.lookup tr stateRefs
7594 execute ::
7695 (MonadIO m) =>
7796 Op Concrete ->
78− m (Opaque (IORef Dynamic))
97+ m (Opaque (NEL.NonEmpty Dynamic))
7998 -- bit subtle here - a chewable v _also_ gets a var, because chewing it should
8099 -- only give subcomponents.
81− execute (Chewable v) = Opaque <$> liftIO (newIORef v)
100+ execute (Chewable offset trs v) = _ -- pure . Opaque $ opaque $ pure v -- pure $ Opaque v -- <$> liftIO (newIORef v)
82101 execute (Op (ApiOffset offset) args) = do
83102 fmap Opaque . liftIO $ do
84− realArgs <- mapM (readIORef . opaque) args
85− let (_offset, (_staticArgs, _ret, endpoint)) = staticRoutes !! offset
103+ let realArgs = map opaque args
104+ let (_offset, (_staticArgs, endpoint)) = staticRoutes !! offset
86105 -- now, magic happens: we apply some dynamic arguments to a dynamic
87106 -- function and hopefully something useful pops out the end.
88107 func = foldr (\arg curr -> flip dynApply arg =<< curr) (Just endpoint) realArgs
89− let showable = map dynTypeRep (endpoint : realArgs)
108+ let showable = "blah" -- map dynTypeRep (_endpoint : realArgs)
90109 case func of
91110 Nothing -> error ("all screwed up: " <> maybe ("nothing: " <> show showable) (show . dynTypeRep) func)
92111 Just (f') -> do
@@ -94,35 +113,43 @@ callEndpoint staticRoutes =
94113 Nothing -> error ("all screwed up: " <> maybe ("nothing: " <> show showable) (show . dynTypeRep) func)
95114 Just f -> liftIO f >>= \case
96115 Left (serverError :: ServerError) -> error (show serverError)
97− Right (_typeRep :: SomeTypeRep, (dyn :: Dynamic)) -> newIORef dyn
116+ Right (_typeRep :: SomeTypeRep, (dyn :: NEL.NonEmpty Dynamic)) -> pure dyn
98117 in Command
99118 gen
100119 execute
101− [ Update $ \s@State {..} op o' ->
102− case op of
103− Chewable v ->
104− let (c : cs) = chewable
105− in s
106− { stateRefs =
107− Map.insertWith (<>) (dynTypeRep v) (pure o') stateRefs,
108− chewable = (breakdownDynamic c) <> cs
109− }
110− (Op (ApiOffset offset) _args) ->
111− s
112− { stateRefs =
113− let (_, (_, tr, _)) = staticRoutes !! offset
114− in Map.insertWith
115− (<>)
116− tr
117− (pure o')
118− stateRefs
119− }
120+ [ update
120121 ]
121122
122−-- | just a stub for now - this is where we probably have to start using ConstrainedDynamic
123−-- so we can pack them in with a Breakdown constraint. TODO.
124−breakdownDynamic :: Dynamic -> [Dynamic]
125−breakdownDynamic = const []
123+update :: Callback Op (Opaque (NEL.NonEmpty Dynamic)) State
124+update = Update $ \s@State {..} op o' ->
125+ case op of
126+ Chewable offset tr v -> case _extract offset trs chewable of
127+ Nothing -> error "internal error"
128+ Just (tr, _extracted, rest) ->
129+ s
130+ { stateRefs =
131+ -- so, what's going on here... we need to be able to grab and merge
132+ -- a list of (typeref, var). this means that we need to be able to pull
133+ -- out typereps symbolically too, we can't just rely on the dynamic being there.
134+ -- Map.insertWith (<>) (dynTypeRep v) _ (pure o') stateRefs,
135+ Map.insertWith (<>) tr (_ o') stateRefs,
136+ chewable = rest
137+ }
138+ (Op (ApiOffset _offset) _args) ->
139+ s
140+ { chewable = _ o' : chewable
141+ -- Map.insertWith
142+ -- (<>)
143+ -- tr
144+ -- (pure o')
145+ -- stateRefs
146+ }
147+
148+extract :: Int -> [a] -> Maybe (a, [a])
149+extract = undefined
150+
151+preload :: (MonadGen n, MonadIO m) => Dynamic -> Command n m State
152+preload = undefined
126153
127154prop_sequential :: forall api. (FlattenServer api, ToReifiedApi (Endpoints api)) => Server api -> [Dynamic] -> PropertyT IO ()
128155prop_sequential server seed = do
@@ -131,20 +158,19 @@ prop_sequential server seed = do
131158 forAll $ do
132159 Gen.sequential
133160 (Range.linear 1 100)
134− (emptyState {chewable = seed})
135− [callEndpoint reifiedApi]
136− executeSequential (emptyState {chewable = seed}) actions
137−
138−prop_concurrent :: forall api. (FlattenServer api, ToReifiedApi (Endpoints api)) => Server api -> [Dynamic] -> PropertyT IO ()
139−prop_concurrent server seed =
140− let reifiedApi = toReifiedApi (flattenServer @api server) (Proxy @(Endpoints api))
141− in do
142− actions <-
143− forAll $
144− Gen.parallel
145− (Range.linear 1 50)
146− (Range.linear 1 10)
147− (emptyState {chewable = seed})
148− [callEndpoint reifiedApi]
149− test $
150− executeParallel (emptyState {chewable = seed}) actions
161+ emptyState
162+ (fmap preload seed <> [callEndpoint reifiedApi])
163+ executeSequential emptyState actions
164+-- prop_concurrent :: forall api. (FlattenServer api, ToReifiedApi (Endpoints api)) => Server api -> [Dynamic] -> PropertyT IO ()
165+-- prop_concurrent server seed =
166+-- let reifiedApi = toReifiedApi (flattenServer @api server) (Proxy @(Endpoints api))
167+-- in do
168+-- actions <-
169+-- forAll $
170+-- Gen.parallel
171+-- (Range.linear 1 50)
172+-- (Range.linear 1 10)
173+-- (emptyState {chewable = seed})
174+-- [callEndpoint reifiedApi]
175+-- test $
176+-- executeParallel (emptyState {chewable = seed}) actions
src/Roboservant/Types.hs+11−9
@@ -23,20 +23,22 @@ module Roboservant.Types
2323where
2424
2525import Data.Dynamic (Dynamic)
26−import Data.IORef (IORef)
2726import Data.List.NonEmpty (NonEmpty)
2827import Data.Map.Strict (Map)
2928import Data.Typeable (TypeRep)
3029import Hedgehog (HTraversable (..), Opaque, Symbolic, Var)
31−import Roboservant.Types.FlattenServer
30+import Roboservant.Types.FlattenServer ( FlattenServer(..) )
3231import Roboservant.Types.ReifiedApi
32+ ( ApiOffset(..), ReifiedApi, ToReifiedApi(..) )
3333
34−-- | preloads are basically done, just waiting for a Var.
35−-- chewable things may have more structure in them.
3634data State v
3735 = State
38− { stateRefs :: Map TypeRep (NonEmpty (Var (Opaque (IORef Dynamic)) v)),
39− chewable :: [Dynamic]
36+ { stateRefs :: Map TypeRep (NonEmpty (Var (Opaque Dynamic) v)),
37+ -- this is unfortunate. ideally we'd keep it structurally linked so there was a typerep
38+ -- for each var, but of course, when it's evaluated symbolically, there is no Var to look at.
39+ -- therefore we maintain the invariant manually
40+ chewable :: [(NonEmpty TypeRep,
41+ Var (Opaque (NonEmpty Dynamic)) v) ]
4042 }
4143
4244emptyState :: forall v. State v
@@ -48,12 +50,12 @@ data Op (v :: * -> *)
4850 = -- this needs to change - need a constructor offset and a list of vars inside,
4951 -- because a single argument to the api could be a sum type with many constructors and varying
5052 -- arguments itself.
51− Op ApiOffset [(Var (Opaque (IORef Dynamic)) v)]
52− | Chewable Dynamic
53+ Op ApiOffset [Var (Opaque Dynamic) v]
54+ | Chewable Int TypeRep (Var (Opaque (Dynamic)) v)
5355
5456deriving instance Show (Op Symbolic)
5557
5658instance HTraversable Op where
5759 htraverse r = \case
5860 Op offset args -> Op offset <$> traverse (htraverse r) args
59− Chewable v -> pure $ Chewable v
61+ Chewable i trs v -> Chewable i trs <$> htraverse r v
src/Roboservant/Types/ReifiedApi.hs+7−7
@@ -33,7 +33,7 @@ newtype ApiOffset = ApiOffset Int
3333 deriving (Eq, Show)
3434 deriving newtype (Enum, Num)
3535
36−type ReifiedEndpoint = ([TypeRep], NonEmpty Dynamic)
36+type ReifiedEndpoint = ([TypeRep], Dynamic)
3737
3838type ReifiedApi = [(ApiOffset, ReifiedEndpoint)]
3939
@@ -78,7 +78,7 @@ instance
7878 ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
7979 where
8080 toReifiedEndpoint endpoint _ = id
81− ([], _ endpoint)
81+ ([], endpoint)
8282
8383instance
8484 (ToReifiedEndpoint endpoint) =>
@@ -107,7 +107,7 @@ instance
107107 where
108108 toReifiedEndpoint endpoint _ =
109109 toReifiedEndpoint endpoint (Proxy @endpoint)
110− & \(args, result, typeRepMap) -> (typeRep (Proxy @Bool) : args, result, typeRepMap)
110+ & \(args, result) -> (typeRep (Proxy @Bool) : args, result)
111111
112112instance
113113 (Typeable (If (FoldRequired mods) paramType (Maybe paramType)), ToReifiedEndpoint endpoint, SBoolI (FoldRequired mods)) =>
@@ -115,7 +115,7 @@ instance
115115 where
116116 toReifiedEndpoint endpoint _ =
117117 toReifiedEndpoint endpoint (Proxy @endpoint)
118− & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldRequired mods) paramType (Maybe paramType))) : args, result, typeRepMap)
118+ & \(args, result) -> (typeRep (Proxy @(If (FoldRequired mods) paramType (Maybe paramType))) : args, result)
119119
120120instance
121121 (Typeable (If (FoldRequired mods) headerType (Maybe headerType)), ToReifiedEndpoint endpoint, SBoolI (FoldRequired mods)) =>
@@ -123,7 +123,7 @@ instance
123123 where
124124 toReifiedEndpoint endpoint _ =
125125 toReifiedEndpoint endpoint (Proxy @endpoint)
126− & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldRequired mods) headerType (Maybe headerType))) : args, result, typeRepMap)
126+ & \(args, result) -> (typeRep (Proxy @(If (FoldRequired mods) headerType (Maybe headerType))) : args, result)
127127
128128instance
129129 (Typeable captureType, ToReifiedEndpoint endpoint) =>
@@ -131,7 +131,7 @@ instance
131131 where
132132 toReifiedEndpoint endpoint _ =
133133 toReifiedEndpoint endpoint (Proxy @endpoint)
134− & \(args, result, typeRepMap) -> (typeRep (Proxy @captureType) : args, result, typeRepMap)
134+ & \(args, result) -> (typeRep (Proxy @captureType) : args, result)
135135
136136instance
137137 (Typeable (If (FoldLenient mods) (Either String requestType) requestType), ToReifiedEndpoint endpoint, SBoolI (FoldLenient mods)) =>
@@ -139,4 +139,4 @@ instance
139139 where
140140 toReifiedEndpoint endpoint _ =
141141 toReifiedEndpoint endpoint (Proxy @endpoint)
142− & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldLenient mods) (Either String requestType) requestType)) : args, result, typeRepMap)
142+ & \(args, result) -> (typeRep (Proxy @(If (FoldLenient mods) (Either String requestType) requestType)) : args, result)
stack.yaml+1−1
@@ -1,4 +1,4 @@
1−resolver: lts-16.14
1+resolver: lts-16.16
22packages:
33- .
44
stack.yaml.lock+4−4
@@ -95,7 +95,7 @@ packages:
9595 commit: cd9012414a93cf08f86e409544d4d3a8f282b507
9696snapshots:
9797- completed:
98− size: 532382
99− url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/14.yaml
100− sha256: 1ef27e36f38824abafc43224ca612211b3828fa9ffd31ba0fc2867ae2e19ba90
101− original: lts-16.14
98+ size: 532380
99+ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/16.yaml
100+ sha256: d6b004b095fe2a0b8b14fbc30014ee97e58843b9c9362ddb9244273dda62649e
101+ original: lts-16.16