Getting associated type synonyms with template Haskell
Asked Answered
I

1

259

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").

Isadora answered 22/6, 2013 at 6:11 Comment(7)
Have you looked at reifyInstances?Vardon
@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.Isadora
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?Vardon
@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 :-)Isadora
@Abel: You spoke too soon :-) I mean, it's not solved, but now we know that it's impossible! Your bounty also got me to file a bug report, #10891.Isadora
@AntalS-Z, perhaps, or it triggered Yuras to do some research and write his reply. To me, from a Q&A standpoint, a good answer can also be it is not possible because.... I'm fine with that. Once (if?) that bug report gets resolved, add a new answer and I'll be happy to award that new answer some bounty again :pWidner
@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 :-)Isadora
S
14

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.

Spier answered 17/9, 2015 at 10:34 Comment(9)
Thank you, Yuras! I've also filed bug report #10891 about this. Note that I don't think it's backwards-incompatible (but don't quote me on that): reify returns a ClassI containing a ClassD which contains a [Dec], and I was expecting one of those Decs to be a FamilyD, which already exists.Isadora
Just what I needed. But I will leave the bounty open until the grace period, just to allow others to comment and/or prove you wrong ;)Widner
@AntalS-Z I mean that FamilyD doesn't support associated type synonym defaults. You are probably not using them, but full solution may require API change.Spier
@Abel, leaving the bounty open till the end also tends to help good answers attract votes, so it's a more effective way to reward a good answer than awarding it quickly.Recognizance
Bounty period expired. This is the best (and only) answer, until or unless the bug report #10891 is resolved. Perhaps a good idea to include a link to the bug report in your answer.Widner
FYI, #10891 is fixed and it's waiting to be merged.Kaki
Any idea why the TH AST differs from the internal one?Oletaoletha
@Oletaoletha AFAIK ghc devs want to change internal AST freely without breaking TH API. Probably there are other reasons too.Spier
I think this (referring to the split between AST implementations) will be rectified as part of Trees that GrowLodgment

© 2022 - 2024 — McMap. All rights reserved.