{- PARADISE generic programming example, modified for timing experiments. We compare three versions of increase and bill, based on: - traditional Data.Generics - Data.Generics.GPS - Uniplate via Data.Generics.PlateData -} module Main where import qualified Data.Generics as DG import Data.Generics.GPS import Data.Generics.PlateData import CompanyDatatypes import System.Time import System.IO import Data.List hiding((\\)) -- add redundancy, so that we've got something substantial to measure genComR = (\(C ds)->C $ take 100000 $ cycle ds) genCom incS :: Integer -> Salary -> Salary incS k (S s) = sk `seq` S sk where sk = s+k -- first, the traditional Syb-style versions {-# SPECIALISE increase :: Integer -> Company -> Company #-} increase :: Data a => Integer -> a -> a increase k = DG.everywhere (DG.mkT (incS k)) {-# SPECIALISE bill :: Company -> Integer #-} bill :: Data a => a -> Integer bill = DG.everything (+) (0 `DG.mkQ` billS) where billS (S s) = s -- second, Syb with GPS (only the qualifiers and types change) {-# SPECIALISE increase' :: Integer -> Company -> Company #-} increase' :: Data a => Integer -> a -> a increase' k = everywhere (mkT (incS k)) {-# SPECIALISE bill' :: Company -> Integer #-} bill' :: Data a => a -> Integer bill' = everything (+) (0 `mkQ` billS) where billS (S s) = s -- TODO: -- - PlateData has a serious performance problem (Neil is aware of it) -- that completely masks the benefit of its type map optimization here. -- - for PlateData, explicitly giving specific types gives more sharing -- of type maps than using a SPECIALISE pragma on general types??? -- - for GPS, we get no sharing of type maps between increase' and bill', -- either way; so further improvements are possible -- third, Uniplate PlateData versions, for comparison {-# SPECIALISE uni_increase :: Integer -> Company -> Company #-} -- uni_increase :: Data a => Integer -> a -> a uni_increase :: Integer -> Company -> Company uni_increase k = transformBi (incS k) {-# SPECIALISE uni_bill :: Company -> Integer #-} -- uni_bill :: Data a => a -> Integer uni_bill :: Company -> Integer uni_bill x = sum [ s | S s <- universeBi x] main = do print genComR t <- getClockTime let genComR' = increase 1 genComR print genComR' t <- showTime "Data.Generics increase: " t hPrint stderr $ bill $ genComR' t <- showTime "Data.Generics bill: " t let genComR' = increase' 1 genComR print genComR' t <- showTime "Data.Generics.GPS increase: " t hPrint stderr $ bill' genComR' showTime "Data.Generics.GPS bill: " t let genComR' = uni_increase 1 genComR print genComR' t <- showTime "Data.Generics.PlateData increase: " t hPrint stderr $ uni_bill genComR' showTime "Data.Generics.PlateData bill: " t where showTime l t = do t' <- getClockTime -- hPrint stderr (t,t') hPrint stderr $ (l++) $ timeDiffToString $ normalizeTimeDiff $ t' `diffClockTimes` t hPutStrLn stderr "" return t'