|
| 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