Skip to content

Commit 79a89bc

Browse files
committed
simplify arbitrary instances for map and set
1 parent 0b474aa commit 79a89bc

File tree

2 files changed

+40
-101
lines changed

2 files changed

+40
-101
lines changed

containers-tests/tests/map-properties.hs

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{-# LANGUAGE CPP #-}
22
{-# LANGUAGE TupleSections #-}
3+
{-# LANGUAGE TypeOperators #-}
4+
{-# LANGUAGE TypeFamilies #-}
35

46
#ifdef STRICT
57
import Data.Map.Strict as Data.Map
@@ -313,46 +315,7 @@ main = defaultMain $ testGroup "map-properties"
313315
, testProperty "mapAccumRWithKey" prop_mapAccumRWithKey
314316
]
315317

316-
{--------------------------------------------------------------------
317-
Arbitrary, reasonably balanced trees
318-
--------------------------------------------------------------------}
319-
320-
-- | The IsInt class lets us constrain a type variable to be Int in an entirely
321-
-- standard way. The constraint @ IsInt a @ is essentially equivalent to the
322-
-- GHC-only constraint @ a ~ Int @, but @ IsInt @ requires manual intervention
323-
-- to use. If ~ is ever standardized, we should certainly use it instead.
324-
-- Earlier versions used an Enum constraint, but this is confusing because
325-
-- not all Enum instances will work properly for the Arbitrary instance here.
326-
class (Show a, Read a, Integral a, Arbitrary a) => IsInt a where
327-
fromIntF :: f Int -> f a
328-
329-
instance IsInt Int where
330-
fromIntF = id
331-
332-
-- | Convert an Int to any instance of IsInt
333-
fromInt :: IsInt a => Int -> a
334-
fromInt = runIdentity . fromIntF . Identity
335-
336-
{- We don't actually need this, but we can add it if we ever do
337-
toIntF :: IsInt a => g a -> g Int
338-
toIntF = unf . fromIntF . F $ id
339-
340-
newtype F g a b = F {unf :: g b -> a}
341-
342-
toInt :: IsInt a => a -> Int
343-
toInt = runIdentity . toIntF . Identity -}
344-
345-
346-
-- How much the minimum key of an arbitrary map should vary
347-
positionFactor :: Int
348-
positionFactor = 1
349-
350-
-- How much the gap between consecutive keys in an arbitrary
351-
-- map should vary
352-
gapRange :: Int
353-
gapRange = 5
354-
355-
instance (IsInt k, Arbitrary v) => Arbitrary (Map k v) where
318+
instance (Int ~ k, Arbitrary v) => Arbitrary (Map k v) where
356319
arbitrary = sized (\sz0 -> do
357320
sz <- choose (0, sz0)
358321
middle <- choose (-positionFactor * (sz + 1), positionFactor * (sz + 1))
@@ -366,7 +329,16 @@ instance (IsInt k, Arbitrary v) => Arbitrary (Map k v) where
366329
diff <- lift $ choose (1, gapRange)
367330
let i' = i + diff
368331
put i'
369-
pure (fromInt i')
332+
pure i'
333+
334+
-- How much the minimum key of an arbitrary map should vary
335+
positionFactor :: Int
336+
positionFactor = 1
337+
338+
-- How much the gap between consecutive keys in an arbitrary
339+
-- map should vary
340+
gapRange :: Int
341+
gapRange = 5
370342

371343
-- A type with a peculiar Eq instance designed to make sure keys
372344
-- come from where they're supposed to.

containers-tests/tests/set-properties.hs

Lines changed: 27 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE TypeOperators #-}
3+
{-# LANGUAGE TypeFamilies #-}
24
import qualified Data.IntSet as IntSet
35
import Data.List (nub, sort, sortBy)
46
import qualified Data.List as List
@@ -192,42 +194,7 @@ test_deleteAt = do
192194
Arbitrary, reasonably balanced trees
193195
--------------------------------------------------------------------}
194196

195-
-- | The IsInt class lets us constrain a type variable to be Int in an entirely
196-
-- standard way. The constraint @ IsInt a @ is essentially equivalent to the
197-
-- GHC-only constraint @ a ~ Int @, but @ IsInt @ requires manual intervention
198-
-- to use. If ~ is ever standardized, we should certainly use it instead.
199-
-- Earlier versions used an Enum constraint, but this is confusing because
200-
-- not all Enum instances will work properly for the Arbitrary instance here.
201-
class (Show a, Read a, Integral a, Arbitrary a) => IsInt a where
202-
fromIntF :: f Int -> f a
203-
204-
instance IsInt Int where
205-
fromIntF = id
206-
207-
-- | Convert an Int to any instance of IsInt
208-
fromInt :: IsInt a => Int -> a
209-
fromInt = runIdentity . fromIntF . Identity
210-
211-
{- We don't actually need this, but we can add it if we ever do
212-
toIntF :: IsInt a => g a -> g Int
213-
toIntF = unf . fromIntF . F $ id
214-
215-
newtype F g a b = F {unf :: g b -> a}
216-
217-
toInt :: IsInt a => a -> Int
218-
toInt = runIdentity . toIntF . Identity -}
219-
220-
221-
-- How much the minimum value of an arbitrary set should vary
222-
positionFactor :: Int
223-
positionFactor = 1
224-
225-
-- How much the gap between consecutive elements in an arbitrary
226-
-- set should vary
227-
gapRange :: Int
228-
gapRange = 5
229-
230-
instance IsInt a => Arbitrary (Set a) where
197+
instance (Int ~ a) => Arbitrary (Set a) where
231198
arbitrary = sized (\sz0 -> do
232199
sz <- choose (0, sz0)
233200
middle <- choose (-positionFactor * (sz + 1), positionFactor * (sz + 1))
@@ -241,45 +208,45 @@ instance IsInt a => Arbitrary (Set a) where
241208
diff <- lift $ choose (1, gapRange)
242209
let i' = i + diff
243210
put i'
244-
pure (fromInt i')
211+
pure i'
245212

246-
data TwoSets = TwoSets (Set Int) (Set Int) deriving (Show)
213+
-- How much the minimum value of an arbitrary set should vary
214+
positionFactor :: Int
215+
positionFactor = 1
247216

248-
data TwoLists a = TwoLists [a] [a]
217+
-- How much the gap between consecutive elements in an arbitrary
218+
-- set should vary
219+
gapRange :: Int
220+
gapRange = 5
249221

250-
data Options2 = One2 | Two2 | Both2 deriving (Bounded, Enum)
251-
instance Arbitrary Options2 where
252-
arbitrary = arbitraryBoundedEnum
222+
data TwoSets = TwoSets (Set Int) (Set Int) deriving (Show)
253223

254224
-- We produce two lists from a simple "universe". This instance
255225
-- is intended to give good results when the two lists are then
256226
-- combined with each other; if other elements are used with them,
257227
-- they may or may not behave particularly well.
258-
instance IsInt a => Arbitrary (TwoLists a) where
259-
arbitrary = sized $ \sz0 -> do
260-
sz <- choose (0, sz0)
261-
let universe = [0,3..3*(fromInt sz - 1)]
262-
divide2Gen universe
263-
264228
instance Arbitrary TwoSets where
265229
arbitrary = do
266-
TwoLists l r <- arbitrary
230+
(l, r) <- sized $ \sz0 -> do
231+
sz <- choose (0, sz0)
232+
let universe = [0,3..3*(sz - 1)]
233+
divide2Gen universe
267234
TwoSets <$> setFromList l <*> setFromList r
268-
269-
divide2Gen :: [a] -> Gen (TwoLists a)
270-
divide2Gen [] = pure (TwoLists [] [])
271-
divide2Gen (x : xs) = do
272-
way <- arbitrary
273-
TwoLists ls rs <- divide2Gen xs
274-
case way of
275-
One2 -> pure (TwoLists (x : ls) rs)
276-
Two2 -> pure (TwoLists ls (x : rs))
277-
Both2 -> pure (TwoLists (x : ls) (x : rs))
235+
where
236+
divide2Gen :: [a] -> Gen ([a], [a])
237+
divide2Gen [] = pure ([], [])
238+
divide2Gen (x : xs) = do
239+
mIsFirst <- arbitrary
240+
(ls, rs) <- divide2Gen xs
241+
pure $ case mIsFirst of
242+
Just True -> ((x : ls), rs)
243+
Just False -> (ls, (x : rs))
244+
Nothing -> ((x : ls), (x : rs))
278245

279246
{--------------------------------------------------------------------
280247
Valid trees
281248
--------------------------------------------------------------------}
282-
forValid :: (IsInt a,Testable b) => (Set a -> b) -> Property
249+
forValid :: (Int ~ a, Testable b) => (Set a -> b) -> Property
283250
forValid f = forAll arbitrary $ \t ->
284251
classify (size t == 0) "empty" $
285252
classify (size t > 0 && size t <= 10) "small" $

0 commit comments

Comments
 (0)