packages feed

unfoldable-0.4.0: src/Data/Unfolder/Arbitrary.hs

{-# LANGUAGE 
    ScopedTypeVariables
  , GeneralizedNewtypeDeriving
  #-}
module Data.Unfolder.Arbitrary where
  
import Control.Applicative
import Data.Unfoldable
import Data.Unfolder
import Test.QuickCheck.Arbitrary
import Test.QuickCheck.Gen

import Control.Monad.Trans.Reader
import Data.Functor.Constant
import Data.Monoid (Sum(..))

-- This is somewhat of a hack. It assumes that choose always chooses from a list of Gen (t a),
-- which is true at the top-level, but might not be when recursing.

newtype CountPos a = CountPos { getCountPos :: Constant (Sum Int, Sum Int, [(Int, Int)]) a }
  deriving (Functor, Applicative)
instance Unfolder CountPos where
  choose ms = CountPos . Constant $ 
    (Sum 0, Sum 1, map (\(CountPos (Constant (Sum c, Sum r, _))) -> (c, r)) ms)

newtype Arb a = Arb { getArb :: ReaderT [(Int, Int)] Gen a }
  deriving (Functor, Applicative)
instance Unfolder Arb where
  choose ms = Arb (ReaderT f)
    where 
      f poss = sized (\n -> oneof . map (resz n) . filter ((<= n) . fst . fst) . zip poss $ ms)
        where
          resz n ((c, r), Arb (ReaderT g)) = resize ((n - c) `div` max r 1) (g poss)

arbitraryDefault :: forall t a. (Unfoldable t, Arbitrary a) => Gen (t a)
arbitraryDefault = flip runReaderT poss . getArb $ unfold (Arb . ReaderT $ const arbitrary)
  where
    CountPos (Constant (_, _, poss)) = 
      unfold (CountPos $ Constant (Sum 1, Sum 0, [])) :: CountPos (t ())