Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Template Haskell generate multi-param typeclass instances?

The latest (2.8.0.0) definition for the Dec has the following instance constructor:

InstanceD Cxt Type [Dec]

Seems that only one type can be instantiated. Is there a way to work around this?

like image 734
bfops Avatar asked Nov 11 '12 14:11

bfops


2 Answers

Yes, multi-parameter type classes are supported.

Somewhat confusingly, the Type argument refers to the entire instance head, and, even though it's not really a type, it looks enough like one syntactically that the type Type was reused for this purpose.

Therefore, if you're generating a multi-parameter instance Foo Int Bool, you need to use the "type" Foo Int Bool, constructed for example like this:

(ConT (mkName "Foo") `AppT` ConT (mkName "Int")) `AppT` ConT (mkName "Bool")

Here's a complete example:

{-# LANGUAGE MultiParamTypeClasses, TemplateHaskell #-}

import Language.Haskell.TH

class Foo a b where
  foo :: (a, b)

$(return [InstanceD [] (((ConT (mkName "Foo")) `AppT` ConT (mkName "Int")) `AppT` ConT (mkName "Bool"))
   [ValD (VarP (mkName "foo")) 
         (NormalB (TupE [LitE (IntegerL 42), ConE (mkName "False")])) []]])

main = print (foo :: (Int, Bool))
like image 86
hammar Avatar answered Sep 20 '22 12:09

hammar


An easy way to answer this and similar is to use runQ with spliced definitions. Eg in ghci:

$ ghci
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
Prelude> :set -XTemplateHaskell
Prelude> :set -XMultiParamTypeClasses 
Prelude> import Language.Haskell.TH
Prelude Language.Haskell.TH> class Class a b where
Prelude Language.Haskell.TH> runQ [d| instance Class Int Bool where |]
[InstanceD [] (AppT (AppT (ConT :Interactive.Class) (ConT GHC.Types.Int)) (ConT GHC.Types.Bool)) []]

This shows the exact form needed, replace Class with whatever class you are using.

like image 42
David Miani Avatar answered Sep 22 '22 12:09

David Miani