@@ -24,6 +24,7 @@ module Constrained.GenT (
2424 suchThatT ,
2525 suchThatWithTryT ,
2626 scaleT ,
27+ resizeT ,
2728 firstGenT ,
2829 tryGenT ,
2930 chooseT ,
@@ -34,7 +35,6 @@ module Constrained.GenT (
3435 vectorOfT ,
3536 listOfUntilLenT ,
3637 listOfT ,
37- resizeT ,
3838 strictGen ,
3939 looseGen ,
4040
@@ -69,6 +69,9 @@ import GHC.Stack
6969import System.Random
7070import Test.QuickCheck hiding (Args , Fun )
7171import Test.QuickCheck.Gen
72+ import Test.QuickCheck.Random
73+ import Control.Arrow (second )
74+ import Control.Monad.Trans
7275
7376-- ==============================================================
7477-- The GE Monad
@@ -90,6 +93,62 @@ instance Monad GE where
9093 GenError es >>= _ = GenError es
9194 Result a >>= k = k a
9295
96+ ------------------------------------------------------------------------
97+ -- Threading gen monad
98+ ------------------------------------------------------------------------
99+
100+ -- The normal Gen monad always splits the seed when doing >>=. This is for very
101+ -- good reasons - it lets you write generators that generate infinite data to
102+ -- the left of a >>= and let's your generators be very lazy!
103+
104+ -- A traditional GenT m a implementation would inherit this splitting behaviour
105+ -- in order to let you keep writing infinite and lazy things to the left of >>=
106+ -- on the GenT m level. Now, the thing to realize about this is that unless
107+ -- your code is very carefully written to avoid it this means you're going to
108+ -- end up with unnecessary >>=s and thus unnecessary splits.
109+
110+ -- To get around this issue of unnecessary splits we introduce a threading GenT
111+ -- implementation here that sacrifices letting you do infinite (and to some
112+ -- extent lazy) structures to the left of >>= on the GenT m level, but doesn't
113+ -- prohibit you from doing so on the Gen level.
114+
115+ -- This drastically reduces the number of seed splits while still letting you
116+ -- write lazy and infinite generators in Gen land by being a little bit more
117+ -- careful. It works great for constrained-generators in particular, which has
118+ -- a tendency to be strict and by design avoids inifinte values.
119+
120+ liftGenToThreading :: Monad m => Gen a -> ThreadingGenT m a
121+ liftGenToThreading g = ThreadingGen $ \ seed size -> do
122+ let (seed', seed'') = split seed
123+ pure (seed'', unGen g seed' size)
124+
125+ runThreadingGen :: Functor m => ThreadingGenT m a -> Gen (m a )
126+ runThreadingGen g = MkGen $ \ seed size -> do
127+ snd <$> unThreadingGen g seed size
128+
129+ strictGetSize :: Applicative m => ThreadingGenT m Int
130+ strictGetSize = ThreadingGen $ \ seed size -> pure (seed, size)
131+
132+ scaleThreading :: (Int -> Int ) -> ThreadingGenT m a -> ThreadingGenT m a
133+ scaleThreading f sg = ThreadingGen $ \ seed size -> unThreadingGen sg seed (f size)
134+
135+ newtype ThreadingGenT m a = ThreadingGen { unThreadingGen :: QCGen -> Int -> m (QCGen , a ) }
136+
137+ instance Functor m => Functor (ThreadingGenT m ) where
138+ fmap f (ThreadingGen g) = ThreadingGen $ \ seed size -> second f <$> g seed size
139+
140+ instance Monad m => Applicative (ThreadingGenT m ) where
141+ pure a = ThreadingGen $ \ seed _ -> pure (seed, a)
142+ (<*>) = ap
143+
144+ instance Monad m => Monad (ThreadingGenT m ) where
145+ ThreadingGen g >>= k = ThreadingGen $ \ seed size -> do
146+ (seed', a) <- g seed size
147+ unThreadingGen (k a) seed' size
148+
149+ instance MonadTrans ThreadingGenT where
150+ lift m = ThreadingGen $ \ seed _ -> (seed,) <$> m
151+
93152------------------------------------------------------------------------
94153-- The GenT monad
95154-- An environment monad on top of GE
@@ -108,19 +167,17 @@ data GenMode
108167
109168-- | A `Gen` monad wrapper that allows different generation modes and different
110169-- failure types.
111- newtype GenT m a = GenT { runGenT :: GenMode -> [NonEmpty String ] -> Gen ( m a ) }
170+ newtype GenT m a = GenT { runGenT :: GenMode -> [NonEmpty String ] -> ThreadingGenT m a }
112171 deriving (Functor )
113172
114173instance Monad m => Applicative (GenT m ) where
115- pure a = GenT (\ _ _ -> pure @ Gen ( pure @ m a) )
174+ pure a = GenT (\ _ _ -> pure a )
116175 (<*>) = ap
117176
118- -- I think this might be an inlined use of the Gen monad transformer?
119177instance Monad m => Monad (GenT m ) where
120- GenT m >>= k = GenT $ \ mode -> \ msgs -> MkGen $ \ r n -> do
121- let (r1, r2) = split r
122- a <- unGen (m mode msgs) r1 n
123- unGen (runGenT (k a) mode msgs) r2 n
178+ GenT m >>= k = GenT $ \ mode msgs -> do
179+ a <- m mode msgs
180+ runGenT (k a) mode msgs
124181
125182instance MonadGenError m => MonadFail (GenT m ) where
126183 fail s = genError s
@@ -168,15 +225,15 @@ instance MonadGenError GE where
168225
169226-- | calls to genError and fatalError, add the stacked messages in the monad.
170227instance MonadGenError m => MonadGenError (GenT m ) where
171- genErrorNE e = GenT $ \ _ xs -> pure $ genErrors (add e xs)
172- genErrors es = GenT $ \ _ xs -> pure $ genErrors (cat es xs)
228+ genErrorNE e = GenT $ \ _ xs -> lift $ genErrors (add e xs)
229+ genErrors es = GenT $ \ _ xs -> lift $ genErrors (cat es xs)
173230
174231 -- Perhaps we want to turn genError into fatalError, if mode_ is Strict?
175- fatalErrorNE e = GenT $ \ _ xs -> pure $ fatalErrors (add e xs)
176- fatalErrors es = GenT $ \ _ xs -> pure $ fatalErrors (cat es xs)
232+ fatalErrorNE e = GenT $ \ _ xs -> lift $ fatalErrors (add e xs)
233+ fatalErrors es = GenT $ \ _ xs -> lift $ fatalErrors (cat es xs)
177234
178235 -- Perhaps we want to turn fatalError into genError, if mode_ is Loose?
179- explainNE e (GenT f) = GenT $ \ mode es -> fmap ( explainNE e) (f mode es)
236+ explainNE e (GenT f) = GenT $ \ mode es -> ThreadingGen $ \ seed size -> explainNE e $ unThreadingGen (f mode es) seed size
180237
181238-- ====================================================
182239-- useful operations on NonEmpty
@@ -270,29 +327,25 @@ listFromGE = fromGE (const []) . explain "listFromGE"
270327-- Useful operations on GenT
271328
272329-- | Run a t`GenT` generator in `Strict` mode
273- strictGen :: GenT m a -> Gen (m a )
274- strictGen genT = runGenT genT Strict []
330+ strictGen :: Functor m => GenT m a -> Gen (m a )
331+ strictGen genT = runThreadingGen $ runGenT genT Strict []
275332
276333-- | Run a t`GenT` generator in `Loose` mode
277- looseGen :: GenT m a -> Gen (m a )
278- looseGen genT = runGenT genT Loose []
334+ looseGen :: Functor m => GenT m a -> Gen (m a )
335+ looseGen genT = runThreadingGen $ runGenT genT Loose []
279336
280337-- | Turn a t`GenT` generator into a `Gen` generator in `Strict` mode
281338genFromGenT :: GenT GE a -> Gen a
282339genFromGenT genT = errorGE <$> strictGen genT
283340
284- -- | Locally change the generation size
285- resizeT :: (Int -> Int ) -> GenT m a -> GenT m a
286- resizeT f (GenT gm) = GenT $ \ mode msgs -> sized $ \ sz -> resize (f sz) (gm mode msgs)
287-
288341-- | Turn a `Gen` generator into a t`GenT` generator that never fails.
289- pureGen :: Applicative m => Gen a -> GenT m a
290- pureGen gen = GenT $ \ _ _ -> pure <$> gen
342+ pureGen :: Monad m => Gen a -> GenT m a
343+ pureGen gen = GenT $ \ _ _ -> liftGenToThreading gen
291344
292345-- | Lift `listOf` to t`GenT`
293346listOfT :: MonadGenError m => GenT GE a -> GenT m [a ]
294347listOfT gen = do
295- lst <- pureGen . listOf $ runGenT gen Loose []
348+ lst <- pureGen . listOf $ runThreadingGen $ runGenT gen Loose []
296349 catGEs lst
297350
298351-- | Generate a list of elements of length at most @goalLen@, but accepting
@@ -310,18 +363,18 @@ listOfUntilLenT gen goalLen validLen =
310363 genList `suchThatT` validLen . length
311364 where
312365 genList = do
313- res <- pureGen . vectorOf goalLen $ runGenT gen Loose []
366+ res <- pureGen . vectorOf goalLen $ runThreadingGen $ runGenT gen Loose []
314367 catGEs res
315368
316369-- | Lift `vectorOf` to t`GenT`
317370vectorOfT :: MonadGenError m => Int -> GenT GE a -> GenT m [a ]
318371vectorOfT i gen = GenT $ \ mode _ -> do
319- res <- fmap sequence . vectorOf i $ runGenT gen Strict []
372+ res <- liftGenToThreading $ fmap sequence . vectorOf i $ runThreadingGen $ runGenT gen Strict []
320373 case mode of
321- Strict -> pure $ runGE res
374+ Strict -> lift $ runGE res
322375 Loose -> case res of
323- FatalError es -> pure $ genErrors es
324- _ -> pure $ runGE res
376+ FatalError es -> lift $ genErrors es
377+ _ -> lift $ runGE res
325378
326379infixl 2 `suchThatT`
327380
@@ -351,16 +404,20 @@ suchThatWithTryT tries g p = do
351404
352405-- | Lift `scale` to t`GenT`
353406scaleT :: (Int -> Int ) -> GenT m a -> GenT m a
354- scaleT sc (GenT gen) = GenT $ \ mode msgs -> scale sc $ gen mode msgs
407+ scaleT sc (GenT gen) = GenT $ \ mode msgs -> scaleThreading sc $ gen mode msgs
408+
409+ -- | Lift `resize` to t`GenT`
410+ resizeT :: Int -> GenT m a -> GenT m a
411+ resizeT = scaleT . const
355412
356413-- | Access the `GenMode` we are running in, useful to decide e.g. if we want
357414-- to re-try in case of a `GenError` or give up
358- getMode :: Applicative m => GenT m GenMode
359- getMode = GenT $ \ mode _ -> pure ( pure mode)
415+ getMode :: Monad m => GenT m GenMode
416+ getMode = GenT $ \ mode _ -> pure mode
360417
361418-- | Get the current stack of `explain` above you
362- getMessages :: Applicative m => GenT m [NonEmpty String ]
363- getMessages = GenT $ \ _ msgs -> pure ( pure msgs)
419+ getMessages :: Monad m => GenT m [NonEmpty String ]
420+ getMessages = GenT $ \ _ msgs -> pure msgs
364421
365422-- | Locally change the generation mode
366423withMode :: GenMode -> GenT m a -> GenT m a
@@ -377,7 +434,7 @@ frequencyT gs = do
377434 msgs <- getMessages
378435 r <-
379436 explain " suchThatT in oneofT" $
380- pureGen (frequency [(f, runGenT g mode msgs) | (f, g) <- gs]) `suchThatT` isOk
437+ pureGen (frequency [(f, runThreadingGen $ runGenT g mode msgs) | (f, g) <- gs]) `suchThatT` isOk
381438 runGE r
382439
383440-- | Lift `choose` to t`GenT`, failing with a `genError` in case of an empty interval
@@ -388,18 +445,16 @@ chooseT (a, b)
388445
389446-- | Get the size provided to the generator
390447sizeT :: Monad m => GenT m Int
391- sizeT = GenT $ \ mode msgs -> sized $ \ n -> runGenT ( pure n) mode msgs
448+ sizeT = GenT $ \ _ _ -> strictGetSize
392449
393450-- ==================================================================
394451-- Reflective analysis of the internal GE structure of (GenT GE x)
395452-- This allows "catching" internal FatalError and GenError, and allowing
396453-- the program to control what happens in those cases.
397454
398455-- | Always succeeds, but returns the internal GE structure for analysis
399- inspect :: forall m x . MonadGenError m => GenT GE x -> GenT m (GE x )
400- inspect (GenT f) = GenT g
401- where
402- g mode msgs = do geThing <- f mode msgs; pure @ Gen (pure @ m geThing)
456+ inspect :: forall m a . MonadGenError m => GenT GE a -> GenT m (GE a )
457+ inspect (GenT f) = GenT $ \ mode msgs -> liftGenToThreading $ runThreadingGen $ f mode msgs
403458
404459-- | Ignore all kinds of Errors, by squashing them into Nothing
405460tryGenT :: MonadGenError m => GenT GE a -> GenT m (Maybe a )
0 commit comments