module Data.Generics.Product.Fields
(
HasField (..)
, GHasField (..)
) where
import Data.Generics.Internal.Families
import Data.Generics.Internal.Lens
import Data.Kind (Constraint, Type)
import GHC.Generics
import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
class HasField (field :: Symbol) a s | s field -> a where
field :: Lens' s a
field f s
= fmap (flip (setField @field) s) (f (getField @field s))
getField :: s -> a
getField s = s ^. field @field
setField :: a -> s -> s
setField = set (field @field)
instance
( Generic s
, ErrorUnless field s (HasTotalFieldP field (Rep s))
, GHasField field (Rep s) a
) => HasField field a s where
field = repIso . gfield @field
type family ErrorUnless (field :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
ErrorUnless field s 'False
= TypeError
( 'Text "The type "
':<>: 'ShowType s
':<>: 'Text " does not contain a field named "
':<>: 'ShowType field
)
ErrorUnless _ _ 'True
= ()
class GHasField (field :: Symbol) (f :: Type -> Type) a | field f -> a where
gfield :: Lens' (f x) a
instance GProductHasField field l r a (HasTotalFieldP field l)
=> GHasField field (l :*: r) a where
gfield = gproductField @field @_ @_ @_ @(HasTotalFieldP field l)
instance (GHasField field l a, GHasField field r a)
=> GHasField field (l :+: r) a where
gfield = combine (gfield @field @l) (gfield @field @r)
instance GHasField field (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 a)) a where
gfield = mIso . kIso
instance GHasField field f a => GHasField field (M1 D meta f) a where
gfield = mIso . gfield @field
instance GHasField field f a => GHasField field (M1 C meta f) a where
gfield = mIso . gfield @field
class GProductHasField (field :: Symbol) l r a (left :: Bool) | left field l r -> a where
gproductField :: Lens' ((l :*: r) x) a
instance GHasField field l a => GProductHasField field l r a 'True where
gproductField = first . gfield @field
instance GHasField field r a => GProductHasField field l r a 'False where
gproductField = second . gfield @field