{-# 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 ]