260

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

Antal Spector-Zabusky
  • 36,191
  • 7
  • 77
  • 140
  • 2
    Have you looked at `reifyInstances`? – Kwarrtz Aug 01 '15 at 05:13
  • 2
    @Kwarrtz: I just tried it now. It doesn't work, though; it just gives rise to the same `InstanceD`s 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 Aug 01 '15 at 05:22
  • 1
    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 Aug 01 '15 at 05:27
  • @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 Aug 01 '15 at 05:34
  • @Abel: [You spoke too soon :-)](http://stackoverflow.com/a/32628240/237428) 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](https://ghc.haskell.org/trac/ghc/ticket/10891). – Antal Spector-Zabusky Sep 17 '15 at 18:00
  • @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 :p – Abel Sep 17 '15 at 18:08
  • 1
    @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 Sep 17 '15 at 18:09

1 Answers1

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.

Yuras
  • 13,856
  • 1
  • 45
  • 58
  • Thank you, Yuras! I've also filed [bug report #10891](https://ghc.haskell.org/trac/ghc/ticket/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`](http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#v:ClassD) which contains a `[Dec]`, and I was expecting one of those `Dec`s to be a [`FamilyD`](http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#v:FamilyD), which already exists. – Antal Spector-Zabusky Sep 17 '15 at 18:06
  • Just what I needed. But I will leave the bounty open [until the grace period](http://blog.stackoverflow.com/2011/09/bounty-reasons-and-post-notices/), just to allow others to comment and/or prove you wrong ;) – Abel Sep 17 '15 at 18:10
  • 1
    @AntalS-Z I mean that `FamilyD` doesn't support [associated type synonym defaults](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/type-families.html#assoc-decl-defs). You are probably not using them, but full solution may require API change. – Yuras Sep 17 '15 at 18:43
  • 5
    @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. – dfeuer Sep 17 '15 at 18:45
  • 1
    Bounty period expired. This is the best (and only) answer, until or unless [the bug report #10891 is resolved](https://ghc.haskell.org/trac/ghc/ticket/10891). Perhaps a good idea to include a link to the bug report in your answer. – Abel Sep 20 '15 at 11:20
  • 1
    FYI, #10891 is fixed and it's waiting to be merged. – sinan Sep 22 '15 at 13:11
  • Any idea why the TH AST differs from the internal one? – SwiftsNamesake Aug 31 '17 at 00:08
  • 1
    @SwiftsNamesake AFAIK ghc devs want to change internal AST freely without breaking TH API. Probably there are other reasons too. – Yuras Aug 31 '17 at 09:49
  • I think this (referring to the split between AST implementations) will be rectified as part of [Trees that Grow](https://ghc.haskell.org/trac/ghc/wiki/ImplementingTreesThatGrow) – Sebastian Graf Sep 14 '17 at 18:45