Skip to content

Commit ad5b2f7

Browse files
authored
Merge pull request #127 from ynishiza/test/strictness-writer
[Test] Strictness tests for Writer
2 parents f1a27e4 + 02ce1b5 commit ad5b2f7

4 files changed

Lines changed: 590 additions & 32 deletions

File tree

‎test/Arbitrary.hs‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{-# LANGUAGE DerivingStrategies #-}
2+
{-# LANGUAGE ExistentialQuantification #-}
3+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+
{-# LANGUAGE InstanceSigs #-}
5+
{-# LANGUAGE RankNTypes #-}
6+
{-# LANGUAGE ScopedTypeVariables #-}
7+
{-# LANGUAGE TypeApplications #-}
8+
9+
module Arbitrary
10+
( Bot (..),
11+
BaseMonad (..),
12+
isStrictMonad,
13+
F1 (..),
14+
F1Bot (..),
15+
withBaseMonad,
16+
)
17+
where
18+
19+
import Control.Exception (evaluate)
20+
21+
import Data.Data
22+
import Data.Functor.Identity (Identity (..))
23+
import Data.Tuple.Solo
24+
25+
import Test.ChasingBottoms.IsBottom (isBottom)
26+
import Test.QuickCheck
27+
28+
bottom :: forall a. a
29+
bottom = error "<bottom>"
30+
31+
-- | Arbitrary (Bot a) values may be bottom.
32+
--
33+
-- Borrowed from container tests: https://github.com/haskell/containers/
34+
newtype Bot a
35+
= Bot { unBot :: a }
36+
37+
data BaseMonad
38+
= forall m. (Typeable m, Monad m) => LazyBaseMonad (forall a. m a -> IO a)
39+
| forall m. (Typeable m, Monad m) => StrictBaseMonad (forall a. m a -> IO a)
40+
41+
-- Use the underlying Monad of a BaseMonad.
42+
withBaseMonad :: BaseMonad -> (forall m. (Monad m) => m a) -> IO a
43+
withBaseMonad (LazyBaseMonad v) = v
44+
withBaseMonad (StrictBaseMonad v) = v
45+
46+
isStrictMonad :: BaseMonad -> Bool
47+
isStrictMonad (LazyBaseMonad _) = False
48+
isStrictMonad (StrictBaseMonad _) = True
49+
50+
instance Arbitrary BaseMonad where
51+
arbitrary =
52+
elements
53+
[ LazyBaseMonad id, -- IO
54+
StrictBaseMonad (evaluate . runIdentity), -- Identity
55+
LazyBaseMonad (evaluate . getSolo), -- Solo
56+
LazyBaseMonad (\f -> evaluate $ f ()) -- constant function () -> a
57+
]
58+
59+
instance Show BaseMonad where
60+
show v = case v of
61+
(StrictBaseMonad m) -> "StrictBaseMonad " <> getName m
62+
(LazyBaseMonad m) -> "LazyBaseMonad " <> getName m
63+
where
64+
getName :: forall m. (Typeable m) => (forall a. m a -> IO a) -> String
65+
getName _ = show $ typeRep (Proxy @m)
66+
67+
instance Show a => Show (Bot a) where
68+
show (Bot x) = if isBottom x then "<bottom>" else show x
69+
70+
instance Arbitrary a => Arbitrary (Bot a) where
71+
arbitrary =
72+
frequency
73+
[ (1, pure bottom),
74+
(4, Bot <$> arbitrary)
75+
]
76+
77+
-- | Arbitrary function of one argument.
78+
newtype F1 a b = F1 { unF1 :: a -> b }
79+
deriving newtype (Arbitrary)
80+
81+
instance (Typeable a, Typeable b) => Show (F1 a b) where
82+
show :: F1 a b -> String
83+
show _ = a <> " -> " <> b
84+
where
85+
a = show $ typeRep (Proxy @a)
86+
b = show $ typeRep (Proxy @b)
87+
88+
-- | Arbitrary function that may bottom in the output.
89+
--
90+
-- To be precise, the function is either
91+
-- a) a valid arbitrary function i.e. never bottoms, or
92+
-- b) a constant function which always bottoms.
93+
--
94+
-- In particular, the QuickCheck built-in Func cannot be used for this, since
95+
-- Func a (Bot b) generates a function which may or may not bottom depending on the input.
96+
newtype F1Bot a b = F1Bot { unF1Bot :: a -> b }
97+
98+
instance (CoArbitrary a, Arbitrary b) => Arbitrary (F1Bot a b) where
99+
arbitrary = do
100+
useBottomFunc <- arbitrary :: Gen Bool
101+
F1Bot <$>
102+
if useBottomFunc
103+
then return (const bottom)
104+
else (arbitrary :: Gen (a -> b))
105+
106+
instance (Typeable a, Typeable b) => Show (F1Bot a b) where
107+
show :: F1Bot a b -> String
108+
show _ = a <> " -> " <> b
109+
where
110+
a = show $ typeRep (Proxy @a)
111+
b = show $ typeRep (Proxy @b)

0 commit comments

Comments
 (0)