Skip to content

Commit a308baf

Browse files
authored
Merge pull request #82 from sharkdp/arbitrary-records
Add 'Arbitrary' instance for records
2 parents 93e9a11 + 85430e7 commit a308baf

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

bower.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
"purescript-random": "^3.0.0",
3333
"purescript-strings": "^3.0.0",
3434
"purescript-transformers": "^3.0.0",
35-
"purescript-generics-rep": "^5.0.0"
35+
"purescript-generics-rep": "^5.0.0",
36+
"purescript-typelevel-prelude": "^2.4.0",
37+
"purescript-record": "^0.2.0"
3638
},
3739
"devDependencies": {
3840
"purescript-assert": "^3.0.0"

src/Test/QuickCheck/Arbitrary.purs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ module Test.QuickCheck.Arbitrary
77
, genericCoarbitrary
88
, class ArbitraryGenericSum
99
, arbitraryGenericSum
10+
, class ArbitraryRowList
11+
, arbitraryRecord
1012
) where
1113

1214
import Prelude
@@ -17,6 +19,7 @@ import Control.Monad.Gen.Common as MGC
1719
import Data.Char (toCharCode, fromCharCode)
1820
import Data.Either (Either(..))
1921
import Data.Foldable (foldl)
22+
import Data.Generic.Rep (class Generic, to, from, NoArguments(..), Sum(..), Product(..), Constructor(..), Argument(..), Rec(..), Field(..))
2023
import Data.Identity (Identity(..))
2124
import Data.Int (toNumber)
2225
import Data.Lazy (Lazy, defer, force)
@@ -25,9 +28,13 @@ import Data.List.NonEmpty (NonEmptyList(..))
2528
import Data.Maybe (Maybe(..))
2629
import Data.Newtype (wrap)
2730
import Data.NonEmpty (NonEmpty(..), (:|))
31+
import Data.Record (insert)
2832
import Data.String (charCodeAt, fromCharArray, split)
33+
import Data.Symbol (class IsSymbol, SProxy(..))
2934
import Data.Tuple (Tuple(..))
30-
import Data.Generic.Rep (class Generic, to, from, NoArguments(..), Sum(..), Product(..), Constructor(..), Argument(..), Rec(..), Field(..))
35+
36+
import Type.Prelude (class RowToList)
37+
import Type.Row (kind RowList, class RowLacks, Nil, Cons, RLProxy(..))
3138

3239
import Test.QuickCheck.Gen (Gen, elements, listOf, chooseInt, sized, perturbGen, repeatable, arrayOf, oneOf, uniform)
3340

@@ -223,3 +230,31 @@ genericArbitrary = to <$> (arbitrary :: Gen rep)
223230
genericCoarbitrary :: forall a rep. Generic a rep => Coarbitrary rep => a -> Gen a -> Gen a
224231
genericCoarbitrary x g = to <$> coarbitrary (from x) (from <$> g)
225232

233+
-- | A helper typeclass to implement `Arbitrary` for records.
234+
class ArbitraryRowList
235+
(list :: RowList)
236+
(row :: # Type)
237+
| list -> row where
238+
arbitraryRecord :: RLProxy list -> Gen (Record row)
239+
240+
instance arbitraryRowListNil :: ArbitraryRowList Nil () where
241+
arbitraryRecord _ = pure {}
242+
243+
instance arbitraryRowListCons ::
244+
( Arbitrary a
245+
, ArbitraryRowList listRest rowRest
246+
, RowLacks key rowRest
247+
, RowCons key a rowRest rowFull
248+
, RowToList rowFull (Cons key a listRest)
249+
, IsSymbol key
250+
) => ArbitraryRowList (Cons key a listRest) rowFull where
251+
arbitraryRecord _ = do
252+
value <- arbitrary
253+
previous <- arbitraryRecord (RLProxy :: RLProxy listRest)
254+
pure $ insert (SProxy :: SProxy key) value previous
255+
256+
instance arbitraryRecordInstance ::
257+
( RowToList row list
258+
, ArbitraryRowList list row
259+
) => Arbitrary (Record row) where
260+
arbitrary = arbitraryRecord (RLProxy :: RLProxy list)

test/Main.purs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ main = do
4646
log "Generating via Generic"
4747
logShow =<< randomSample' 10 (arbitrary :: Gen (Foo Int))
4848

49+
log "Arbitrary instance for records"
50+
listOfRecords ← randomSample' 10 (arbitrary :: Gen { foo :: Int, nested :: { bar :: Boolean } })
51+
let toString rec = "{ foo: " <> show rec.foo <> "; nested.bar: " <> show rec.nested.bar <> " }"
52+
logShow (toString <$> listOfRecords)
53+
4954
quickCheck \(x :: Int) -> x <? x + 1
5055
quickCheck \(x :: Int) -> x <=? x + 1
5156
quickCheck \(x :: Int) -> x >=? x - 1

0 commit comments

Comments
 (0)