Skip to content

Commit d5b4e06

Browse files
committed
Generalize generators from NonEmpty to Foldable1
1 parent d216c3b commit d5b4e06

File tree

2 files changed

+60
-39
lines changed

2 files changed

+60
-39
lines changed

bower.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@
1616
"package.json"
1717
],
1818
"dependencies": {
19-
"purescript-nonempty": "^4.0.0",
19+
"purescript-nonempty": "matthewleon/purescript-nonempty#Foldable1",
2020
"purescript-tailrec": "^3.0.0",
2121
"purescript-tuples": "^4.0.0",
2222
"purescript-unfoldable": "^3.0.0",
23-
"purescript-integers": "^3.0.0"
23+
"purescript-integers": "^3.0.0",
24+
"purescript-foldable-traversable": "^3.3.0"
2425
},
2526
"devDependencies": {
2627
"purescript-console": "^3.0.0",
2728
"purescript-assert": "^3.0.0",
2829
"purescript-arrays": "^4.2.1",
2930
"purescript-transformers": "^3.4.0",
3031
"purescript-math": "^2.1.0"
32+
},
33+
"resolutions": {
34+
"purescript-nonempty": "Foldable1"
3135
}
3236
}

src/Control/Monad/Gen.purs

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ import Prelude
1212

1313
import Control.Monad.Gen.Class (class MonadGen, Size, chooseBool, chooseFloat, chooseInt, resize, sized)
1414
import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM)
15-
16-
import Data.Foldable (class Foldable, length, foldl, foldMap)
17-
import Data.Maybe (Maybe(..), fromMaybe)
15+
import Data.Foldable (foldMap, length)
16+
import Data.Maybe (Maybe(..))
1817
import Data.Monoid.Additive (Additive(..))
1918
import Data.Newtype (alaF)
20-
import Data.NonEmpty (NonEmpty, (:|))
19+
import Data.Semigroup.Foldable (class Foldable1, foldMap1)
2120
import Data.Tuple (Tuple(..), fst, snd)
2221
import Data.Unfoldable (class Unfoldable, unfoldr)
2322

@@ -30,10 +29,29 @@ choose genA genB = chooseBool >>= if _ then genA else genB
3029

3130
-- | Creates a generator that outputs a value chosen from a selection of
3231
-- | existing generators with uniform probability.
33-
oneOf :: forall m f a. MonadGen m => Foldable f => NonEmpty f (m a) -> m a
34-
oneOf (x :| xs) = do
35-
n <- chooseInt 0 (length xs)
36-
if n < 1 then x else fromIndex (n - 1) x xs
32+
oneOf :: forall m f a. MonadGen m => Foldable1 f => f (m a) -> m a
33+
oneOf xs = do
34+
n <- chooseInt 0 (length xs - 1)
35+
fromIndex n xs
36+
37+
newtype FreqSemigroup a = FreqSemigroup (Number -> Tuple (Maybe Number) a)
38+
39+
freqSemigroup :: forall a. Tuple Number a -> FreqSemigroup a
40+
freqSemigroup (Tuple weight x) =
41+
FreqSemigroup \pos ->
42+
if pos >= weight
43+
then Tuple (Just (pos - weight)) x
44+
else Tuple Nothing x
45+
46+
getFreqVal :: forall a. FreqSemigroup a -> Number -> a
47+
getFreqVal (FreqSemigroup f) = snd <<< f
48+
49+
instance semigroupFreqSemigroup :: Semigroup (FreqSemigroup a) where
50+
append (FreqSemigroup f) (FreqSemigroup g) =
51+
FreqSemigroup \pos ->
52+
case f pos of
53+
Tuple (Just pos') _ -> g pos'
54+
result -> result
3755

3856
-- | Creates a generator that outputs a value chosen from a selection of
3957
-- | existing generators, where the selection has weight values for the
@@ -42,35 +60,19 @@ oneOf (x :| xs) = do
4260
frequency
4361
:: forall m f a
4462
. MonadGen m
45-
=> Foldable f
46-
=> NonEmpty f (Tuple Number (m a))
63+
=> Foldable1 f
64+
=> f (Tuple Number (m a))
4765
-> m a
48-
frequency (x :| xs) =
49-
let
50-
first = fst x
51-
total = first + alaF Additive foldMap fst xs
52-
in
53-
chooseFloat 0.0 total >>= pick
54-
where
55-
pick pos =
56-
let
57-
initial = go (Tuple 0.0 $ snd x) x
58-
in
59-
snd $ foldl go initial xs
60-
where
61-
go (Tuple weight val) (Tuple currWeight currVal) =
62-
let
63-
nextWeight = weight + currWeight
64-
in
65-
if weight <= pos && pos <= nextWeight
66-
then Tuple nextWeight currVal
67-
else Tuple nextWeight val
66+
frequency xs =
67+
let total = alaF Additive foldMap fst xs
68+
in chooseFloat 0.0 total >>= getFreqVal (foldMap1 freqSemigroup xs)
69+
6870
-- | Creates a generator that outputs a value chosen from a selection with
6971
-- | uniform probability.
70-
elements :: forall m f a. MonadGen m => Foldable f => NonEmpty f a -> m a
71-
elements (x :| xs) = do
72-
n <- chooseInt 0 (length xs)
73-
pure if n == 0 then x else fromIndex (n - 1) x xs
72+
elements :: forall m f a. MonadGen m => Foldable1 f => f a -> m a
73+
elements xs = do
74+
n <- chooseInt 0 (length xs - 1)
75+
pure $ fromIndex n xs
7476

7577
-- | Creates a generator that produces unfoldable structures based on an
7678
-- | existing generator for the elements.
@@ -108,7 +110,22 @@ suchThat gen pred = tailRecM go unit
108110
go :: Unit -> m (Step Unit a)
109111
go _ = gen <#> \a -> if pred a then Done a else Loop unit
110112

111-
fromIndex :: forall f a. Foldable f => Int -> a -> f a -> a
112-
fromIndex i a = fromMaybe a <<< snd <<< (foldl go (Tuple 0 (Just a)))
113+
-- | Internal: used by fromIndex
114+
newtype AtIndex a = AtIndex (Int -> a)
115+
116+
instance semigroupAtIndex :: Semigroup (AtIndex a)
113117
where
114-
go (Tuple ix v) x = Tuple (ix + 1) (if ix == i then Just x else v)
118+
append (AtIndex f) (AtIndex g) =
119+
AtIndex \i -> if i <= 0 then f i else g (i - 1)
120+
121+
atIndex :: forall a. a -> AtIndex a
122+
atIndex = AtIndex <<< const
123+
124+
getAtIndex :: forall a. AtIndex a -> Int -> a
125+
getAtIndex (AtIndex f) = f
126+
127+
-- | Internal: get the Foldable element at index i.
128+
-- | If the index is <= 0, return the first element.
129+
-- | If it's >= length, return the last.
130+
fromIndex :: forall f a. Foldable1 f => Int -> f a -> a
131+
fromIndex i xs = getAtIndex (foldMap1 atIndex xs) i

0 commit comments

Comments
 (0)