Skip to content

GHC.Records.HasField doesn't affect Generic representation

Summary

HasField instance doesn't affect generic representation of the type. Generic based code is not working as expected e.g. Aeson skips these virtual fields and generic-lens accessing virtual field causes compilation error.

Steps to reproduce

module Lib where

import Control.Lens
import Data.Aeson
import Data.Generics.Labels ()
import GHC.Generics
import GHC.OverloadedLabels
import GHC.Records
import Prelude

data Foo = Foo { f00 :: Int } deriving (Show, Eq)
data Bar = Bar { b00 :: Int } deriving (Show, Eq)

instance HasField "bar" Foo Bool where
  getField _ = True

deriving instance Generic Foo
deriving instance Generic Bar
$ cabal repl
ghci> GHC.Generics.from (Bar 33)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 33}}}}
ghci> GHC.Generics.from (Foo 33)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 33}}}}
ghci> encode (genericToJSON defaultOptions (Foo 333))
"{\"f00\":333}"
ghci> GR.getField @"bar" (Foo 333) 
True
ghci>  (Foo 333) ^. #f00
333
ghci>  (Foo 333) ^. #bar

<interactive>:79:15: error: [GHC-64725]
    • The type Foo does not contain a field named 'bar'.
    • In the second argument of ‘(^.)’, namely ‘#bar’
      In the expression: (Foo 333) ^. #bar
      In an equation for ‘it’: it = (Foo 333) ^. #bar

Expected behavior

ghci> GHC.Generics.from (Foo 33)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 33}} :*: M1 {unM1 = K1 {unK1 = True}}}}
ghci>  (Foo 333) ^. #bar
True

Sounds like a big change, but HasField instance promises to give a new field.

Multiple instances of HasField should be appended to data constructor in the alphabetical order.

Environment

  • GHC version used: 9.8.1

Optional:

  • Operating System: Debian 10
  • System Architecture: x86-64
Edited by Daniil Iaitskov
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information