Skip to content

Commit 5dd691a

Browse files
committed
Simpler shrinkRecord
1 parent 354fb6f commit 5dd691a

File tree

1 file changed

+47
-18
lines changed

1 file changed

+47
-18
lines changed

src/Data/API/Tools/QuickCheck.hs

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,33 +94,62 @@ gen_sr_ab = mkTool $ \ ts (an, sr) -> mkArbitraryInstance ts (nodeRepT an) (bdy
9494
-- to generate a list of lists, each sublist being the shrinking of a single
9595
-- individual field, and finally mconcat everything together.
9696
-- Example:
97-
-- shrink = \(Foo a b c) ->
98-
-- (Foo <$> shrink a <*> pure b <*> pure c) ++
99-
-- (Foo <$> pure a <*> shrink b <*> pure c) ++
100-
-- (Foo <$> pure a <*> pure b <*> shrink c)
97+
--
98+
-- shrink = \ x ->
99+
-- case x of
100+
-- Foo a b c ->
101+
-- concat [ Foo <$> shrink a <*> pure b <*> pure c
102+
-- , Foo <$> pure a <*> shrink b <*> pure c
103+
-- , Foo <$> pure a <*> pure b <*> shrink c
104+
-- ]
105+
--
101106
shrinkRecord :: APINode -> SpecRecord -> ExpQ
102107
shrinkRecord an sr = do
103-
x <- newName "x"
104-
-- Matches the fields of the record with fresh variables
105-
-- [( "field1", "field1"), ("field2", "field2") ... ]
106-
recordPatterns <-
107-
forM (srFields sr) $ \(fn,_) -> do
108-
let freshRecName = pref_field_nm an fn
109-
freshPatName <- nodeFieldP an fn
110-
pure (freshRecName,freshPatName)
108+
-- List of field names in the record
109+
let fields :: [Name]
110+
fields = map (pref_field_nm an . fst) (srFields sr)
111+
112+
-- Given a list of fields with a distinguished element, construct
113+
-- Foo <$> pure x0 <*> ... <*> shrink xM <*> ... <*> pure xN
114+
-- where the boolean indicates which field should use 'shrink'.
115+
let shrinkMarkedField :: [(Bool, Name)] -> ExpQ
116+
shrinkMarkedField flds =
117+
applicativeE (nodeConE an) $
118+
flip map flds $ \(shrunk, fld) ->
119+
if shrunk then [e| QC.shrink $(varE fld) |]
120+
else [e| pure $(varE fld) |]
121+
122+
-- Construct the list
123+
-- [ Foo <$> shrink a <*> pure b <*> ...
124+
-- , Foo <$> pure a <*> shrink b <*> ...
125+
-- , ...
126+
-- ]
127+
let shrinkAllFields :: ExpQ
128+
shrinkAllFields = listE (map shrinkMarkedField (distinguishedElements fields))
111129

130+
x <- newName "x"
112131
lamE [varP x] $
113132
caseE (varE x) [
114-
-- temporary, not correct. it won't shrink properly.
115-
match (recP nm (map pure recordPatterns))
116-
(normalB $ applicativeE (nodeConE an) $
117-
flip map recordPatterns $ \(fld, _pat) ->
118-
[e| QC.shrink $(varE fld) |]
119-
) []
133+
-- Foo a b c -> concat [...]
134+
match (recP nm (map (\n -> fieldPat n (varP n)) fields))
135+
(normalB [e| concat $shrinkAllFields |])
136+
[]
120137
]
121138
where
122139
nm = rep_type_nm an
123140

141+
-- | Turn an N-element list into N lists of N pairs, each of which has a single
142+
-- distinguished element marked True.
143+
--
144+
-- >>> distinguishedElements "abc"
145+
-- [[(True,'a'),(False,'b'),(False,'c')],[(False,'a'),(True,'b'),(False,'c')],[(False,'a'),(False,'b'),(True,'c')]]
146+
--
147+
distinguishedElements :: [a] -> [[(Bool, a)]]
148+
distinguishedElements [] = []
149+
distinguishedElements (x:xs) = ((True, x) : map ((,) False) xs)
150+
: map ((False, x) :) (distinguishedElements xs)
151+
152+
124153
-- | Generate an 'Arbitrary' instance for a union:
125154
--
126155
-- > instance Arbitrary Foo where

0 commit comments

Comments
 (0)