I'm trying to create a type signature for a function in template haskell. Is there an easy way of doing this?
I've done some workarounds to solve it in the meantime, but it should be easier, right?
-- TH.hs
module Lib.TH (mkFunction) where
import Language.Haskell.TH
mkFunction n = do
let name = mkName n
[d|
$( ... ) :: Integer -> Integer
$(varP name) = \x -> x + 2|]
-- Other.hs
import TH
mkFunction "test"
What should I write in the $( ... )
above? Everything I've tried results in
Invalid type signature: ... :: Integer -> Integer
Should be of form <variable> :: <type>
I am no TH expert, but I found a way by digging around in the docs and following the type errors.
import Language.Haskell.TH
import Control.Applicative ((<$>))
mkFunction n = do
let name = mkName n
[d|
$( return . SigD name <$> [t| Integer -> Integer |] )
$(varP name) = \x -> x + 2 |]
I don't know if there is a cleaner way.
NOTE this works on 7.8.3 but not 7.10.2. :-(
I don't think (guess) there is a way to splice the name in a Template Haskell type signature (for GHC ≤ 7.10). To still use the quasi-quoting, you can try to process the AST afterwards to set the name where appropriate (and leave the rest untouched):
setName :: Name -> DecsQ -> DecsQ
setName = fmap . map . sn
where sn n (SigD _ t) = SigD n t
sn n (ValD (VarP _) x y) = ValD (VarP n) x y
sn _ d = d
mkFunction n = setName (mkName n)
[d|
n :: Integer -> Integer
n = \x -> x + 2 |]
This was tested on GHC 7.10, and will probably work on past and future versions of GHC with minor modifications.
To what extent this is less or more verbose than writing the AST directly is arguable. It will depend on the frequency you write declarations like that and the complexity of the quoted code.
The above setName
function will obviously break if you use it on a declaration with multiple functions (or recursive functions). To tackle that, you could write a reName
function in the same spirit.
Another workaround. Using applicative syntax and refactoring @luqui's workaround you get something close to what you want:
import Language.Haskell.TH
mkFunction n = do
let name = mkName n
(:) <$> name `sigD` [t| Integer -> Integer |]
<*> [d| $(varP name) = \x -> x + 2 |]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With