11

I'm trying to create the ring Z/n (like normal arithmetic, but modulo some integer). An example instance is Z4:

instance Additive.C Z4 where
  zero = Z4 0
  (Z4 x) + (Z4 y) = Z4 $ (x + y) `mod` 4

And so on for the ring. I'd like to be able to quickly generate these things, and I think the way to do it is with template haskell. Ideally I'd like to just go $(makeZ 4) and have it spit out the code for Z4 like I defined above.

I'm having a lot of trouble with this though. When I do genData n = [d| data $n = $n Integer] I get "parse error in data/newtype declaration". It does work if I don't use variables though: [d| data Z5 = Z5 Integer |], which must mean that I'm doing something weird with the variables. I'm not sure what though; I tried constructing them via newName and that didn't seem to work either.

Can anyone help me with what's going on here?

Xodarap
  • 11,581
  • 11
  • 56
  • 94

1 Answers1

13

The Template Haskell documentation lists the things you are allowed to splice.

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]

In both occurrences of $n, however, you're trying to splice a name.

This means you can't do this using quotations and splices. You'll have to build declaration using the various combinators available in the Language.Haskell.TH module.

I think this should be equivalent to what you're trying to do.

genData :: Name -> Q [Dec]
genData n = fmap (:[]) $ dataD (cxt []) n []
                           [normalC n [strictType notStrict [t| Integer |]]] []

Yep, it's a bit ugly, but there you go. To use this, call it with a fresh name, e.g.

$(genData (mkName "Z5"))
hammar
  • 138,522
  • 17
  • 304
  • 385
  • Could you include an example of its use? I modified what you had slightly and just threw `$(genData "Foo")` as a top-level into my code, but if I do `:i Foo` in ghci it doesn't find anything. – Xodarap Sep 27 '11 at 14:21
  • 1
    @Xodarap: Use `mkName` to make a `Name` from a `String`. I've added an example. I guess you may have used `newName`, which adds some stuff at the end to ensure that the name is unique, so `:info` would not show it. You should be able to see it using `:browse`, though. – hammar Sep 27 '11 at 14:39
  • Thanks! this was my problem exactly. – Xodarap Sep 27 '11 at 17:20