quickcheck-arbitrary-template-0.2.0.0: src/Test/QuickCheck/TH/Generators/Internal/BuildArbitrary.hs
{-# LANGUAGE TemplateHaskell #-}
module Test.QuickCheck.TH.Generators.Internal.BuildArbitrary where
import Language.Haskell.TH
import Safe
-- | automatically build functions named `buildArbn` where n is an integer
-- greater than 0. $(buildArbAny 3) creates a function buildArb3 which takes
-- a constructor that takes 3 parameters, and returns an arbitrary instances
-- of that constructor. It assumes that the constructors type is an instance
-- of Arbitrary.
--
-- buildArb1 :: Arbitrary a
-- => (a -> b)
-- -> Gen b
-- buildArb1 f = f <$> arbitrary
--
-- buildArb2 :: (Arbitrary a, Arbitrary a1)
-- => (a1 -> a -> b)
-- -> Gen b
-- buildArb2 f = f <$> arbitrary <*> arbitrary
buildArbAny :: Int -> Q [Dec]
buildArbAny l
| l < 1 = return []
| otherwise = do
-- name of the function to be returned
let buildArbName = mkName ("buildArb" ++ show l)
-- names of parameters used in the buildArbn function
arbParameterNames <- mapM newName $ (("a" ++) . show) <$> [0..(l-1)] -- one for each parameter of f
let mFirstArbParameterName = headMay arbParameterNames
mRestArbParameterNames = tailMay arbParameterNames
bName <- newName "b" -- the return type of buildArbn
fName <- newName "f" -- the sum type constructor
-- types used in the buildArbn function
mArbitraryTypeName <- lookupTypeName "Arbitrary"
mGenTypeName <- lookupTypeName "Gen"
-- functions used in the buildArbn function
mFmapName <- lookupValueName "<$>"
mApName <- lookupValueName "<*>"
mArbitraryValue <- lookupValueName "arbitrary"
-- check if all the types and functions were found
case (,,,,,,) <$> mFirstArbParameterName <*> mRestArbParameterNames <*> mArbitraryTypeName <*> mGenTypeName <*> mFmapName <*> mApName <*> mArbitraryValue of
Nothing -> return []
Just (firstArbParameterName,restArbParameterNames,arbitraryTypeName,genTypeName,fmapName,apName,arbitraryValue) -> do
-- all of the variables in the function to be created, input and output
let plainTVs = PlainTV <$> (arbParameterNames ++ [bName])
-- Arbitrary type instance required for all vars in arbParameterNames
typeClassRequirements = (AppT (ConT arbitraryTypeName) ) <$> (VarT <$> arbParameterNames)
genB = AppT (ConT genTypeName) (VarT bName)
-- last parameter and return type
-- (a -> Gen b)
aToGenB = (AppT
(AppT
ArrowT
(VarT firstArbParameterName))
(VarT bName))
-- fold function
buildFunctionArgument old new = (AppT
(AppT
ArrowT
(VarT new))
old)
-- build the rest of the parameters
preFunctionArgument = AppT ArrowT $ foldl buildFunctionArgument
aToGenB
restArbParameterNames
-- TH encoding for function arguments
functionArgument = AppT preFunctionArgument genB
-- build the function body
-- f <$> arbitrary
fFmapArbitrary = (InfixE (Just (VarE fName)) (VarE fmapName) (Just (VarE arbitraryValue)))
arbRs = replicate (length arbParameterNames - 1) arbitraryValue
-- build rest of body by folding <*> arbitrary
preFunctionBody = foldl (\old new -> InfixE (Just old) (VarE apName) (Just (VarE new)))
fFmapArbitrary
arbRs
-- TH encoding for function body
functionBody = FunD buildArbName [Clause [VarP fName] (NormalB preFunctionBody) []]
return
[SigD
buildArbName
(ForallT
plainTVs
typeClassRequirements
functionArgument
)
, functionBody
]