259
votes

Can Template Haskell find out the names and/or the declarations of the associated type synonyms declared in a type class? I expected reify would do what I want, but it doesn't seem to provide all the necessary information. It works for getting function type signatures:

% ghci
GHCi, version 7.8.3: http://www.haskell.org/ghc/  :? for help
...
Prelude> -- I'll be inserting line breaks and whitespace for clarity
Prelude> -- in all GHCi output.
Prelude> :set -XTemplateHaskell 
Prelude> import Language.Haskell.TH
Prelude Language.Haskell.TH> class C a where f :: a -> Int
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''C)
ClassI (ClassD [] Ghci1.C [PlainTV a_1627398388] []
               [SigD Ghci1.f
                     (ForallT [PlainTV a_1627398388]
                              [ClassP Ghci1.C [VarT a_1627398388]]
                              (AppT (AppT ArrowT (VarT a_1627398388))
                                    (ConT GHC.Types.Int)))])
       []

However, adding an associated type synonym to the class causes no change (up to renaming) in the output:

Prelude Language.Haskell.TH> :set -XTypeFamilies 
Prelude Language.Haskell.TH> class C' a where type F a :: * ; f' :: a -> Int
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''C')
ClassI (ClassD [] Ghci3.C' [PlainTV a_1627405973] []
               [SigD Ghci3.f'
                     (ForallT [PlainTV a_1627405973]
                              [ClassP Ghci3.C' [VarT a_1627405973]]
                              (AppT (AppT ArrowT (VarT a_1627405973))
                                    (ConT GHC.Types.Int)))])
       []

If I know the name of F, I can look up information about it:

Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''F)
FamilyI (FamilyD TypeFam
                 Ghci3.F
                 [PlainTV a_1627405973]
                 (Just StarT))
        []

But I can't find the name of F in the first place. Even if I add an instance of the type class, the InstanceD has none of the information about the definition:

Prelude Language.Haskell.TH> instance C' [a] where type F [a] = a ; f' = length
Prelude Language.Haskell.TH> f' "Haskell"
7
Prelude Language.Haskell.TH> 42 :: F [Integer]
42
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''C')
ClassI (ClassD [] Ghci3.C' [PlainTV a_1627405973] []
               [SigD Ghci3.f'
                     (ForallT [PlainTV a_1627405973]
                              [ClassP Ghci3.C' [VarT a_1627405973]]
                              (AppT (AppT ArrowT (VarT a_1627405973))
                                    (ConT GHC.Types.Int)))])
       [InstanceD []
                  (AppT (ConT Ghci3.C')
                        (AppT ListT (VarT a_1627406161)))
                  []]

If reify won't work, is there a workaround, other than listing the associate type synonyms manually?

This problem is present in GHC 7.8.3 with version 2.9.0.0 of the template-haskell package; it was also present in GHC 7.4.2 with version 2.7.0.0 of the template-haskell package. (I didn't check on GHC 7.6.*, but I imagine it was present there too.) I'm interested in solutions for any version of GHC (including "this was only fixed in GHC version V").

1
Have you looked at reifyInstances?Kwarrtz
@Kwarrtz: I just tried it now. It doesn't work, though; it just gives rise to the same InstanceDs as I saw with reify: putStrLn $(stringE . show =<< reifyInstances ''C' =<< sequence [[t|[Int]|]]) evaluates to [InstanceD [] (AppT (ConT Ghci1.C') (AppT ListT (VarT a_1627405978))) []], which lacks the type family instances.Antal Spector-Zabusky
I find it odd that reify doesn't return the necessary information. Perhaps show is hiding some of the information? Have you tried examining the Info object directly?Kwarrtz
@Kwarrtz: I'm afraid Info's Show instance is just the derived one, and same for the Show instance for Dec. However, I can also check directly, as you asked, and no: putStrLn $(reify ''C' >>= \i -> case i of ClassI (ClassD _ _ _ _ [SigD _ _]) _ -> stringE "just a SigD" ; _ -> stringE "something else") produces just a SigD – that's really the only thing in the [Dec] in the ClassD! (requires LambdaCase). I agree it's odd; that's why I asked this question :-)Antal Spector-Zabusky
@Abel: I think we're in violent agreement – your original comment said it wasn't enough to attract a brilliant idea, but it did attract Yuras's answer! I absolutely agree about what a good answer is :-)Antal Spector-Zabusky

1 Answers

15
votes

It is not implemented because nobody requested it.

The odd thing is that TH uses its own AST, which doesn't follow internal compiler's AST. As a result, any new feature (e.g. associated type families) is not automatically available via TH. Some one have to open a ticket and implement it.

For the reference: internal reifyClass function ignores associated type families (it is the 5th element of the tuple returned by classExtraBigSig, see also definition of ClassATItem.)

Technically it should be easy to implement associated type family support in reify, but most likely it will require backward incompatible changes in TH API, e.g. because its AST doesn't seem to support associated type defaults.

Added: It is now implemented (without API change btw) and probably will be available in the next ghc release.