haskelltemplate-haskell

How do I get all instances of HasField type class?


Asking ghci about instances of a record type R does not return the instances of HasField.

data R = MkR {f1 :: Int, f2 :: Int}

instance HasField "f3" R Int where
  getField (MkR a b) = a + b

-- ghci
>:i R
type R :: *
data R = MkR {f1 :: Int, f2 :: Int}
-- I'd expect instances for HasField <f1 | f2 | f3>
-- But they aren't in the output

I assume is due to the multiparam nature of such a class, so I tried to write a template haskell snnipet following this other SO answer. The thing is that I am unable to make it work... It just return the empty list.

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DataKinds #-}

import Language.Haskell.TH
    ( stringL,
      Exp(LitE),
      Q,
      Type(ConT),
      Name,
      InstanceDec,
      reifyInstances )
import GHC.Records (HasField (getField))

getFieldIntances :: Name -> Q [InstanceDec]
getFieldIntances name = reifyInstances ''HasField  [ConT name] -- Is this even correct?? I guess ConT is the constructor for "type constructors"

showFieldInstances :: Name -> Q Exp
showFieldInstances name = LitE . stringL . show <$> getFieldIntances name

data R = MkR {f1 :: Int, f2 :: Int}

instance HasField "f3" R Int where
  getField (MkR a b) = a + b

-- If hls eval pluging is activated
-- >>> $(showFieldInstances ''R)
-- "[]"

I have zero knowledge of template haskell. Is there any way to do this?

Thanks in advance

EDIT

I have an ilumination that reifyInstances only works for "fully applied" type classes, so I modify:

getFieldIntances :: Name -> Q [InstanceDec]
getFieldIntances name = reifyInstances ''HasField  [ VarT ( mkName "a") , ConT name, VarT ( mkName "b")]

-- ...

-- >>> $(showFieldInstances ''R)
-- "[InstanceD Nothing [] (AppT (AppT (AppT (ConT GHC.Records.HasField) (LitT (StrTyLit \"f3\"))) (ConT Main.R)) (ConT GHC.Types.Int)) []]"

Which kind of works, but only for explicit instances (virtual fields). can I get ghc generated ones?


Solution

  • You need :info!:

    > :info! R
    type R :: *
    data R = MkR {f1 :: Int, f2 :: Int}
        -- Defined at test.hs:4:1
    instance [safe] HasField "f3" R Int -- Defined at test.hs:6:10
    

    The manual describes the difference:

    :info[!] ⟨name⟩
    ...
    For types and classes, GHCi also summarises instances that mention them. To avoid showing irrelevant information, an instance is shown only if (a) its head mentions ⟨name⟩, and (b) all the other things mentioned in the instance are in scope (either qualified or otherwise) as a result of a :load or :module commands.

    The command :info! works in a similar fashion but it removes restriction (b), showing all instances that are in scope and mention ⟨name⟩ in their head.

    Okay, so what's not in scope? This one is a bit tricky! Thanks to HTNW in the comments for the explanation. Check this out:

    > :i HasField
    type HasField :: forall k. k -> * -> * -> Constraint
    

    Despite the fact that you generally call HasField with three arguments, it actually has four. In HasField "f3" R Int, there is an implicit argument Symbol naming the kind of "f3" (this is k in the declaration above). If you bring Symbol into scope, then :i again works:

    > import GHC.TypeLits (Symbol)
    GHC.TypeLits> :i R
    type R :: *
    data R = MkR {f1 :: Int, f2 :: Int}
        -- Defined at test.hs:4:1
    instance [safe] HasField "f3" R Int -- Defined at test.hs:6:10