Skip to content

Commit 36f36f0

Browse files
authored
Merge pull request #20 from purescript/fix-size-issues
Fix size issues (looping `unfoldable`, sub-zero `genNonEmpty` sizes)
2 parents a44ecb4 + 357dc3f commit 36f36f0

File tree

6 files changed

+103
-9
lines changed

6 files changed

+103
-9
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ install:
1111
- chmod a+x $HOME/purescript
1212
- npm install -g bower
1313
- npm install
14-
- bower install
14+
- bower install --production
1515
script:
1616
- npm run -s build
17+
- bower install
18+
- npm run -s test
1719
after_success:
1820
- >-
1921
test $TRAVIS_TAG &&

bower.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@
2626
"purescript-tailrec": "^4.0.0",
2727
"purescript-tuples": "^5.0.0",
2828
"purescript-unfoldable": "^4.0.0"
29+
},
30+
"devDependencies": {
31+
"purescript-assert": "^4.1.0",
32+
"purescript-console": "^4.2.0",
33+
"purescript-lcg": "^2.0.0"
2934
}
3035
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"private": true,
33
"scripts": {
44
"clean": "rimraf output && rimraf .pulp-cache",
5-
"build": "pulp build -- --censor-lib --strict"
5+
"build": "pulp build -- --censor-lib --strict",
6+
"test": "pulp test"
67
},
78
"devDependencies": {
89
"eslint": "^4.19.1",

src/Control/Monad/Gen.purs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ unfoldable
9191
unfoldable gen = unfoldr unfold <$> sized (tailRecM loopGen <<< Tuple Nil)
9292
where
9393
loopGen :: Tuple (LL a) Int -> m (Step (Tuple (LL a) Int) (LL a))
94-
loopGen = case _ of
95-
Tuple acc 0 ->
96-
pure $ Done acc
97-
Tuple acc n -> do
98-
x <- gen
99-
pure $ Loop (Tuple (Cons x acc) (n - 1))
94+
loopGen (Tuple acc n)
95+
| n <= 0 =
96+
pure $ Done acc
97+
| otherwise = do
98+
x <- gen
99+
pure $ Loop (Tuple (Cons x acc) (n - 1))
100100
unfold :: LL a -> Maybe (Tuple a (LL a))
101101
unfold = case _ of
102102
Nil -> Nothing

src/Control/Monad/Gen/Common.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,4 @@ genNonEmpty
6464
=> Unfoldable f
6565
=> m a
6666
-> m (NonEmpty f a)
67-
genNonEmpty gen = (:|) <$> gen <*> resize (_ - 1) (unfoldable gen)
67+
genNonEmpty gen = (:|) <$> gen <*> resize (max 0 <<< (_ - 1)) (unfoldable gen)

test/Main.purs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
module Test.Main where
2+
3+
import Prelude
4+
5+
import Control.Monad.Gen as Gen
6+
import Control.Monad.Gen.Common as GenC
7+
import Control.Monad.Rec.Class (class MonadRec, tailRecM)
8+
import Data.Bifunctor (bimap)
9+
import Data.Int as Int
10+
import Data.NonEmpty (NonEmpty, (:|))
11+
import Data.Tuple (Tuple(..), snd)
12+
import Effect (Effect)
13+
import Effect.Class (class MonadEffect, liftEffect)
14+
import Effect.Class.Console (log)
15+
import Random.LCG as LCG
16+
import Test.Assert (assertEqual)
17+
18+
main :: Effect Unit
19+
main = do
20+
21+
seed ← LCG.randomSeed
22+
23+
runTestGen seed do
24+
25+
log $ show seed
26+
27+
log "`unfoldable` should not loop on negative size"
28+
nil :: Array UnitGen.resize (const (-1)) $ Gen.unfoldable (pure unit)
29+
liftEffect $ assertEqual { actual: nil, expected: [] }
30+
31+
log "`genNonEmpty` should not reduce the remainder size below zero"
32+
one :: NonEmpty Array IntGen.resize (const 0) $ GenC.genNonEmpty (Gen.sized pure)
33+
liftEffect $ assertEqual { actual: one, expected: 0 :| [] }
34+
35+
--------------------------------------------------------------------------------
36+
37+
type GenState = Tuple LCG.Seed Int
38+
39+
newtype TestGen a = TestGen (GenState -> Effect (Tuple GenState a))
40+
41+
derive instance functorTestGen :: Functor TestGen
42+
43+
instance applyTestGen :: Apply TestGen where
44+
apply = ap
45+
46+
instance applicativeTestGen :: Applicative TestGen where
47+
pure a = TestGen (pure <<< flip Tuple a)
48+
49+
instance bindTestGen :: Bind TestGen where
50+
bind gen f =
51+
TestGen \s ->
52+
unTestGen gen s >>= \(Tuple s' v) ->
53+
unTestGen (f v) s'
54+
55+
instance monadTestGen :: Monad TestGen
56+
57+
instance monadRecTestGen :: MonadRec TestGen where
58+
tailRecM f a = TestGen (tailRecM go <<< flip Tuple a)
59+
where
60+
go (Tuple s a') =
61+
unTestGen (f a') s >>= \(Tuple s' m) ->
62+
pure $ bimap (Tuple s') (Tuple s') m
63+
64+
instance monadGenTestGen :: Gen.MonadGen TestGen where
65+
chooseInt a b = Int.round <$> Gen.chooseFloat (Int.toNumber a) (Int.toNumber b)
66+
chooseFloat a b = uniform <#> \n -> a + (b - a) * n
67+
chooseBool = (_ < 0.5) <$> uniform
68+
resize f (TestGen k) = TestGen \(Tuple seed size) -> k (Tuple seed (f size))
69+
sized f = TestGen \s -> case f (snd s) of TestGen k -> k s
70+
71+
instance monageEffectTestGen :: MonadEffect TestGen where
72+
liftEffect a = TestGen \st -> Tuple st <$> a
73+
74+
lcgStep :: TestGen Int
75+
lcgStep =
76+
TestGen \(Tuple seed size) ->
77+
pure $ Tuple (Tuple (LCG.lcgNext seed) size) (LCG.unSeed seed)
78+
79+
uniform :: TestGen Number
80+
uniform = lcgStep <#> (\n -> Int.toNumber n / Int.toNumber LCG.lcgM)
81+
82+
unTestGen :: forall a. TestGen a -> GenState -> Effect (Tuple GenState a)
83+
unTestGen (TestGen k) = k
84+
85+
runTestGen :: forall a. LCG.Seed -> TestGen a -> Effect a
86+
runTestGen seed gen = snd <$> unTestGen gen (Tuple seed 5)

0 commit comments

Comments
 (0)