Execute a function n times, where n is known at compile time
Your implementation is fine. But it is a little bit ironic: In your SO answer, you start of with "you can use better functions", yet you rewrite nTimes via go. If this was a real module, you would probably want to export both functions nTimesTH and nTimes, and implement the former via the latter:

{-# LANGUAGE TemplateHaskell #-}
module Times (nTimes, nTimesTH) where

import Control.Monad (when)
import Language.Haskell.TH

-- example implementation
nTimes :: Int -&gt; (a -&gt; a) -&gt; a -&gt; a
nTimes n f x = iterate f x !! max 0 n

nTimesTH :: Int -&gt; Q Exp
nTimesTH n = do
  when (n &lt;= 0)    (reportWarning "nTimesTH: argument non-positive")
  when (n &gt;= 1000) (reportWarning "nTimesTH: argument large, can lead to memory exhaustion")

  f &lt;- newName "f"
  x &lt;- newName "x"

  lamE (map varP [f,x]) $ nTimes n (appE (varE f)) (varE x)


This removes the need for code duplication, and any future optimization you find for nTimes gets automatically applied in nTimesTH, although that only matter during the compile time. After all $(nTimesTH n) is completely evaluated during compilation.

Other than that, I moved the warnings on top of the function, since this brings the names f and x closer to their actual use. I also replaced the data constructors with their respective functions.

Note that for a real module you still want to add documentation:

-- | @'nTimes' n f x@ applies @f@ @n@ times over @x@. 
-- It does not apply @f@ if @n@ is negative. In this case @x@ is returned.
--
-- prop&gt; nTimes n f x == iterate f x !! max 0
nTimes :: Int -&gt; (a -&gt; a) -&gt; a -&gt; a

-- | @'nTimesTH' n@ returns a function that applies the first 
--   argument @n@ times to the second argument. 
--   @n@ must be known at compile time. Needs @-XTemplateHaskell@.
--
-- prop&gt; $(nTimesTH n) f x == nTimes n f x
nTimesTH :: Int -&gt; Q Exp