I've just started to use Template Haskell (I've finally got a use case, yay!) and now I'm cognitively stuck.
What I'm trying to do is generating a singleton datatype declaration of the form
data $V = $V deriving (Eq,Ord)
starting from a name V
(hopefully starting with an uppercase character!). To be explicit, I'm trying to write a function declareSingleton
of type String -> DecsQ
(I should mention here I'm using GHC 7.6.1, template-haskell version 2.8.0.0) such that the splice
$(declareSingleton "Foo")
is the equivalent of
data Foo = Foo deriving (Eq,Ord)
I've got the following code working and doing what I want, but I'm not very happy with it:
declareSingleton :: String -> Q [Dec]
declareSingleton s = let n = mkName s in sequence [
dataD (cxt []) n [] [normalC n []] [''Eq,''Ord]
]
I was hoping to get something like the following to work:
declareSingleton :: String -> Q [Dec]
declareSingleton s = let n = mkName s in
[d| data $n = $n deriving (Eq,Ord) |]
I've tried, to no avail (but not exhaustively!), various combinations of $s
, $v
, $(conT v)
, v
, 'v
so I have to suppose my mental model of how Template Haskell works is too simplistic.
Am I missing something obvious here, am I confusing type names and constructor names in some essential way, and can I write declareSingleton
in a nice(r) way?
If so, how; if not, why not?
(Side remark: the Template Haskell API changes rapidly, and I'm happy for that - I want this simple type to eventually implement a multi-parameter type class with an associated type family - but the churn the API is currently going through doesn't make it easy to search for tutorials! There's a huge difference how TH was implemented in 6.12.1 or 7.2 (when most of the existing tutorial were written) versus how it works nowadays...)
From the Template Haskell documentation:
A splice can occur in place of
- an expression; the spliced expression must have type Q Exp
- an type; the spliced expression must have type Q Typ
- a list of top-level declarations; the spliced expression must have type Q [Dec]
So e.g. constructor names simply cannot be spliced in the current version of Template Haskell.
I don't think there's much you can do to simplify this use-case (barring constructing the whole declaration as a string and transforming it into a Dec
via toDec
in haskell-src-meta
).
You might consider simply binding the different parts of the declaration to local variables. While more verbose, it makes the code a bit easier to read.
declareSingleton :: String -> Q [Dec]
declareSingleton s = return [DataD context name vars cons derives] where
context = []
name = mkName s
vars = []
cons = [NormalC name fields]
fields = []
derives = [''Eq, ''Ord]
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