on master help sign in
Make compilation faster by not going through an intermediate HList

In the previous version, the `HasField`, `HasType` and `HasPosition` lenses were implemented using the same underlying class, `GHasKey`, which first flattens the type out to a heterogeneous list, then uniformly indexes into that list. This transformation (and especially optimising it away) takes a significant time. Instead, we can do this directly on the generic tree by providing a predicate that determines which field to focus on. This results in faster compile times, as the number of inlining passes required for optimising away the overhead is greatly reduced.

commit771506b9e43394ea0ec655b46139f5cf82617fc7 authorCsongor Kiss <kiss.csongor.kiss@gmail.com> dateTue 7 Aug 2018, 21:38 (8 years ago) parent6787a4e
10 files changed+237−122
generic-lens.cabal+1−1 src/Data/Generics/Internal/Families/Has.hs+52−9 src/Data/Generics/Product/Fields.hs+10−8 src/Data/Generics/Product/Internal/GLens.hs+79 src/Data/Generics/Product/Internal/Keyed.hs−60 src/Data/Generics/Product/Internal/List.hs+3−1 src/Data/Generics/Product/Internal/Positions.hs+45−26 src/Data/Generics/Product/Internal/Subtype.hs+11−6 src/Data/Generics/Product/Positions.hs+28−8 src/Data/Generics/Product/Typed.hs+8−3
generic-lens.cabal+1−1
@@ -59,11 +59,11 @@ library
5959 , Data.Generics.Sum.Internal.Subtype
6060
6161 , Data.Generics.Product.Internal.Positions
62+ , Data.Generics.Product.Internal.GLens
6263 , Data.Generics.Product.Internal.Subtype
6364
6465 , Data.Generics.Product.Internal.Constraints
6566 , Data.Generics.Product.Internal.List
66− , Data.Generics.Product.Internal.Keyed
6767
6868 build-depends: base >= 4.9 && <= 5.0
6969 , profunctors >= 5.0 && <= 6.0
src/Data/Generics/Internal/Families/Has.hs+52−9
@@ -16,6 +16,9 @@
1616-----------------------------------------------------------------------------
1717module Data.Generics.Internal.Families.Has
1818 ( HasTotalFieldP
19+ , HasTotalTypeP
20+ , HasTotalPositionP
21+ , Pos
1922 , HasPartialTypeP
2023 , HasCtorP
2124 , GTypes
@@ -24,10 +27,13 @@ module Data.Generics.Internal.Families.Has
2427import Data.Type.Bool (type (||), type (&&))
2528import Data.Type.Equality (type (==))
2629import GHC.Generics
27−import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
30+import GHC.TypeLits (Symbol, Nat)
31+import Data.Kind (Type)
2832
2933import Data.Generics.Product.Internal.List
3034
35+-- Note: these could be factored out into a single traversal
36+
3137type family HasTotalFieldP (field :: Symbol) f :: Bool where
3238 HasTotalFieldP field (S1 ('MetaSel ('Just field) _ _ _) _)
3339 = 'True
@@ -41,17 +47,54 @@ type family HasTotalFieldP (field :: Symbol) f :: Bool where
4147 = HasTotalFieldP field f
4248 HasTotalFieldP field (D1 _ f)
4349 = HasTotalFieldP field f
44− HasTotalFieldP field (Rec0 _)
50+ HasTotalFieldP field (K1 _ _)
4551 = 'False
4652 HasTotalFieldP field U1
4753 = 'False
4854 HasTotalFieldP field V1
4955 = 'False
50− HasTotalFieldP field f
51− = TypeError
52− ( 'ShowType f
53− ':<>: 'Text " is not a valid GHC.Generics representation type"
54− )
56+
57+type family HasTotalTypeP (typ :: Type) f :: Bool where
58+ HasTotalTypeP typ (S1 _ (K1 _ typ))
59+ = 'True
60+ HasTotalTypeP typ (l :*: r)
61+ = HasTotalTypeP typ l || HasTotalTypeP typ r
62+ HasTotalTypeP typ (l :+: r)
63+ = HasTotalTypeP typ l && HasTotalTypeP typ r
64+ HasTotalTypeP typ (S1 _ _)
65+ = 'False
66+ HasTotalTypeP typ (C1 _ f)
67+ = HasTotalTypeP typ f
68+ HasTotalTypeP typ (D1 _ f)
69+ = HasTotalTypeP typ f
70+ HasTotalTypeP typ (K1 _ _)
71+ = 'False
72+ HasTotalTypeP typ U1
73+ = 'False
74+ HasTotalTypeP typ V1
75+ = 'False
76+
77+data Pos (p :: Nat)
78+
79+type family HasTotalPositionP (pos :: Nat) f :: Bool where
80+ HasTotalPositionP pos (S1 _ (K1 (Pos pos) _))
81+ = 'True
82+ HasTotalPositionP pos (l :*: r)
83+ = HasTotalPositionP pos l || HasTotalPositionP pos r
84+ HasTotalPositionP pos (l :+: r)
85+ = HasTotalPositionP pos l && HasTotalPositionP pos r
86+ HasTotalPositionP pos (S1 _ _)
87+ = 'False
88+ HasTotalPositionP pos (C1 _ f)
89+ = HasTotalPositionP pos f
90+ HasTotalPositionP pos (D1 _ f)
91+ = HasTotalPositionP pos f
92+ HasTotalPositionP pos (K1 _ _)
93+ = 'False
94+ HasTotalPositionP pos U1
95+ = 'False
96+ HasTotalPositionP pos V1
97+ = 'False
5598
5699type family HasPartialTypeP a f :: Bool where
57100 HasPartialTypeP t (l :+: r)
@@ -73,10 +116,10 @@ type family HasCtorP (ctor :: Symbol) f :: Bool where
73116 HasCtorP ctor _
74117 = 'False
75118
76−type family GTypes (rep :: * -> *) :: [((), *)] where
119+type family GTypes (rep :: Type -> Type) :: [((), Type)] where
77120 GTypes (l :*: r)
78121 = GTypes l ++ GTypes r
79− GTypes (Rec0 a)
122+ GTypes (K1 _ a)
80123 = '[ '( '(), a)]
81124 GTypes (M1 _ m a)
82125 = GTypes a
src/Data/Generics/Product/Fields.hs+10−8
@@ -40,7 +40,7 @@ module Data.Generics.Product.Fields
4040import Data.Generics.Internal.Families
4141import Data.Generics.Internal.VL.Lens as VL
4242import Data.Generics.Internal.Void
43−import Data.Generics.Product.Internal.Keyed
43+import Data.Generics.Product.Internal.GLens
4444
4545import Data.Kind (Constraint, Type)
4646import GHC.Generics
@@ -129,9 +129,9 @@ setField = VL.set (field' @f)
129129instance
130130 ( Generic s
131131 , ErrorUnless field s (CollectField field (Rep s))
132− , GHasKey' field (Rep s) a
132+ , GLens' (HasTotalFieldPSym field) (Rep s) a
133133 ) => HasField' field s a where
134− field' f s = VL.ravel (repLens . gkey @field) f s
134+ field' f s = VL.ravel (repLens . glens @(HasTotalFieldPSym field)) f s
135135
136136instance -- see Note [Changing type parameters]
137137 ( Generic s
@@ -146,14 +146,13 @@ instance -- see Note [Changing type parameters]
146146#endif
147147 , Generic s'
148148 , Generic t'
149− , GHasKey' field (Rep s) a
150− , GHasKey' field (Rep s') a'
151− , GHasKey' field (Rep t') b'
152− , GHasKey field (Rep s) (Rep t) a b
149+ , GLens' (HasTotalFieldPSym field) (Rep s') a'
150+ , GLens' (HasTotalFieldPSym field) (Rep t') b'
151+ , GLens (HasTotalFieldPSym field) (Rep s) (Rep t) a b
153152 , t ~ Infer s a' b
154153 , s ~ Infer t b' a
155154 ) => HasField field s t a b where
156− field f s = VL.ravel (repLens . gkey @field) f s
155+ field f s = VL.ravel (repLens . glens @(HasTotalFieldPSym field)) f s
157156
158157-- -- See Note [Uncluttering type signatures]
159158instance {-# OVERLAPPING #-} HasField f (Void1 a) (Void1 b) a b where
@@ -180,3 +179,6 @@ type family ErrorUnless (field :: Symbol) (s :: Type) (stat :: TypeStat) :: Cons
180179
181180 ErrorUnless _ _ ('TypeStat '[] '[] _)
182181 = ()
182+
183+data HasTotalFieldPSym :: Symbol -> (TyFun (Type -> Type) Bool)
184+type instance Eval (HasTotalFieldPSym sym) tt = HasTotalFieldP sym tt
src/Data/Generics/Product/Internal/GLens.hsnew file+79
@@ -0,0 +1,79 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE TypeInType #-}
3+{-# LANGUAGE AllowAmbiguousTypes #-}
4+{-# LANGUAGE ConstraintKinds #-}
5+{-# LANGUAGE DataKinds #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE KindSignatures #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE Rank2Types #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE UndecidableInstances #-}
16+
17+-----------------------------------------------------------------------------
18+-- |
19+-- Module : Data.Generics.Product.Internal.GLens
20+-- Copyright : (C) 2017 Csongor Kiss
21+-- License : BSD3
22+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
23+-- Stability : experimental
24+-- Portability : non-portable
25+--
26+-- Derive record field getters and setters generically.
27+--
28+-----------------------------------------------------------------------------
29+
30+module Data.Generics.Product.Internal.GLens
31+ ( GLens (..)
32+ , GLens'
33+ , TyFun
34+ , Eval
35+ ) where
36+
37+import Data.Generics.Internal.Profunctor.Lens (Lens, choosing, first, second)
38+import Data.Generics.Internal.Profunctor.Iso (kIso, sumIso, mIso)
39+
40+import Data.Kind (Type)
41+import GHC.Generics
42+
43+type TyFun a b = a -> b -> Type
44+type family Eval (f :: TyFun a b) (x :: a) :: b
45+
46+-- A generic lens that uses some predicate to determine which field to focus on
47+class GLens (pred :: TyFun (Type -> Type) Bool) (s :: Type -> Type) (t :: Type -> Type) a b | s pred -> a, t pred -> b where
48+ glens :: Lens (s x) (t x) a b
49+
50+type GLens' pred s a = GLens pred s s a a
51+
52+instance GProductLens (Eval pred l) pred l r l' r' a b
53+ => GLens pred (l :*: r) (l' :*: r') a b where
54+
55+ glens = gproductField @(Eval pred l) @pred
56+ {-# INLINE glens #-}
57+
58+instance (GLens pred l l' a b, GLens pred r r' a b) => GLens pred (l :+: r) (l' :+: r') a b where
59+ glens = sumIso . choosing (glens @pred) (glens @pred)
60+ {-# INLINE glens #-}
61+
62+instance GLens pred (K1 r a) (K1 r b) a b where
63+ glens = kIso
64+ {-# INLINE glens #-}
65+
66+instance (Functor g, GLens pred f g a b) => GLens pred (M1 m meta f) (M1 m meta g) a b where
67+ glens = mIso . glens @pred
68+ {-# INLINE glens #-}
69+
70+class GProductLens (left :: Bool) (pred :: TyFun (Type -> Type) Bool) l r l' r' a b | pred l r -> a, pred l' r' -> b where
71+ gproductField :: Lens ((l :*: r) x) ((l' :*: r') x) a b
72+
73+instance GLens pred l l' a b => GProductLens 'True pred l r l' r a b where
74+ gproductField = first . glens @pred
75+ {-# INLINE gproductField #-}
76+
77+instance GLens pred r r' a b => GProductLens 'False pred l r l r' a b where
78+ gproductField = second . glens @pred
79+ {-# INLINE gproductField #-}
src/Data/Generics/Product/Internal/Keyed.hsdeleted−60
@@ -1,60 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ConstraintKinds #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeInType #-}
11−{-# LANGUAGE TypeOperators #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−
14−#if __GLASGOW_HASKELL__ == 802
15−{-# OPTIONS_GHC -fno-solve-constant-dicts #-}
16−#endif
17−
18−--------------------------------------------------------------------------------
19−-- |
20−-- Module : Data.Generics.Product.Internal.Keyed
21−-- Copyright : (C) 2018 Csongor Kiss
22−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
23−-- License : BSD3
24−-- Stability : experimental
25−-- Portability : non-portable
26−--
27−--------------------------------------------------------------------------------
28−
29−module Data.Generics.Product.Internal.Keyed
30− ( GHasKey (..)
31− , GHasKey'
32− ) where
33−
34−import Data.Generics.Product.Internal.List
35−import Data.Kind
36−import GHC.Generics
37−import Data.Generics.Internal.Profunctor.Lens
38−import Data.Generics.Internal.Profunctor.Iso
39−
40−class GHasKey (key :: k) (s :: Type -> Type) (t :: Type -> Type) a b | s key -> a, t key -> b where
41− gkey :: Lens (s x) (t x) a b
42−
43−type GHasKey' key s a = GHasKey key s s a a
44−
45−instance (GHasKey key l l' a b, GHasKey key r r' a b) => GHasKey key (l :+: r) (l' :+: r') a b where
46− gkey = sumIso . choosing (gkey @key) (gkey @key)
47− {-# INLINE gkey #-}
48−
49−instance (GHasKey key f g a b) => GHasKey key (M1 D meta f) (M1 D meta g) a b where
50− gkey = ravel (mLens . gkey @key)
51− {-# INLINE gkey #-}
52−
53−instance
54− ( Elem as key i a
55− , Elem bs key i b
56− , IndexList i as bs a b
57− , GIsList k f g as bs
58− ) => GHasKey (key :: k) (M1 C meta f) (M1 C meta g) a b where
59− gkey = mLens . glist @k . point @i
60− {-# INLINE gkey #-}
src/Data/Generics/Product/Internal/List.hs+3−1
@@ -40,8 +40,10 @@ module Data.Generics.Product.Internal.List
4040 , TupleToList
4141 ) where
4242
43−import GHC.TypeLits
43+#if __GLASGOW_HASKELL__ < 804
4444import Data.Semigroup
45+#endif
46+import GHC.TypeLits
4547
4648import Data.Kind (Type)
4749import GHC.Generics
src/Data/Generics/Product/Internal/Positions.hs+45−26
@@ -1,3 +1,4 @@
1+{-# LANGUAGE TypeInType #-}
12{-# LANGUAGE AllowAmbiguousTypes #-}
23{-# LANGUAGE ConstraintKinds #-}
34{-# LANGUAGE DataKinds #-}
@@ -27,41 +28,61 @@
2728-----------------------------------------------------------------------------
2829
2930module Data.Generics.Product.Internal.Positions
30− ( GHasPosition (..)
31− , GHasPosition'
32− , type (<?)
31+ ( type (<?)
3332 , Size
33+ , CRep
3434 ) where
3535
36−import Data.Generics.Product.Internal.List
36+import Data.Generics.Internal.Families.Has (Pos)
3737import Data.Kind (Type)
3838import Data.Type.Bool (If, Not)
3939import GHC.Generics
40−import GHC.TypeLits (type (<=?), type (+), type (-), Nat)
41−import Data.Generics.Internal.Profunctor.Lens
42−import Data.Generics.Internal.Profunctor.Iso
40+import GHC.TypeLits (type (<=?), type (+), Nat)
4341
44−-- |As 'HasPosition' but over generic representations as defined by
45−-- "GHC.Generics".
46−class GHasPosition (i :: Nat) (s :: Type -> Type) (t :: Type -> Type) a b | s i -> a, t i -> b, s t i -> b where
47− gposition :: Lens (s x) (t x) a b
42+-- | Alias for the kind of the generic rep
43+type G k = k -> Type
4844
49−type GHasPosition' i s a = GHasPosition i s s a a
45+--------------------------------------------------------------------------------
5046
51−instance (GHasPosition i l l' a b, GHasPosition i r r' a b) => GHasPosition i (l :+: r) (l' :+: r') a b where
52− gposition = \x -> sumIso (choosing (gposition @i) (gposition @i) x)
53− {-# INLINE gposition #-}
47+-- | In-order labeling of the generic tree with the field positions
48+--
49+-- We replace the (K1 R a) nodes with (K1 (Pos n) a), where 'n' is the position
50+-- of the field in question in the data type. This is convenient, because we
51+-- can reuse all the existing functions as long as they are polymorphic in the
52+-- first parameter of 'K1'.
53+type family CRep (a :: Type) :: G k where
54+ CRep rep = Fst (Traverse (Rep rep) 1)
5455
55−instance GHasPosition i s t a b => GHasPosition i (M1 D meta s) (M1 D meta t) a b where
56− gposition x = mLens (gposition @i x)
57− {-# INLINE gposition #-}
56+-- | The actual traversal.
57+--
58+-- Might be cleaner if the sum and product parts were separated (as there's
59+-- and invariant that 'n' should be zero when we're at a sum node, which holds
60+-- for derived Generic instances (where the sums are strictly above the products))
61+type family Traverse (a :: G k) (n :: Nat) :: (G k, Nat) where
62+ Traverse (M1 mt m s) n
63+ = Traverse1 (M1 mt m) (Traverse s n)
64+ Traverse (l :+: r) n
65+ = '(Fst (Traverse l n) :+: Fst (Traverse r n), n)
66+ Traverse (l :*: r) n
67+ = TraverseProd (:*:) (Traverse l n) r
68+ Traverse (K1 _ p) n
69+ = '(K1 (Pos n) p, n + 1)
70+ Traverse U1 n
71+ = '(U1, n)
5872
59−instance
60− ( IndexList (i - 1) as bs a b
61− , GIsList () f g as bs
62− ) => GHasPosition i (M1 C meta f) (M1 C meta g) a b where
63− gposition x = mIso (glist @() (point @(i - 1) x))
64− {-# INLINE gposition #-}
73+type family Traverse1 (w :: G k -> G k) (z :: (G k, Nat)) :: (G k, Nat) where
74+ Traverse1 w '(i, n) = '(w i, n)
75+
76+-- | For products, we first traverse the left-hand side, followed by the second
77+-- using the counter returned by the left traversal.
78+type family TraverseProd (c :: G k -> G k -> G k) (a :: (G k, Nat)) (r :: G k) :: (G k, Nat) where
79+ TraverseProd w '(i, n) r = Traverse1 (w i) (Traverse r n)
80+
81+--------------------------------------------------------------------------------
82+-- Utilities
83+
84+type family Fst (p :: (a, b)) :: a where
85+ Fst '(a, b) = a
6586
6687type family Size f :: Nat where
6788 Size (l :*: r)
@@ -75,8 +96,6 @@ type family Size f :: Nat where
7596 Size f
7697 = 1
7798
78−--------------------------------------------------------------------------------
79−
8099type x <? y = Not (y <=? x)
81100infixl 4 <?
82101
src/Data/Generics/Product/Internal/Subtype.hs+11−6
@@ -1,3 +1,4 @@
1+{-# LANGUAGE TypeInType #-}
12{-# LANGUAGE AllowAmbiguousTypes #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleContexts #-}
@@ -31,11 +32,12 @@ module Data.Generics.Product.Internal.Subtype
3132 ) where
3233
3334import Data.Generics.Internal.Families
34−import Data.Generics.Product.Internal.Keyed
35+import Data.Generics.Product.Internal.GLens
3536
3637import Data.Kind (Type)
3738import GHC.Generics
38−import Data.Generics.Internal.Profunctor.Lens
39+import GHC.TypeLits (Symbol)
40+import Data.Generics.Internal.Profunctor.Lens (view)
3941
4042--------------------------------------------------------------------------------
4143-- * Generic upcasting
@@ -47,10 +49,10 @@ instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
4749 gupcast rep = gupcast rep :*: gupcast rep
4850
4951instance
50− GHasKey field sub sub t t
52+ GLens' (HasTotalFieldPSym field) sub t
5153 => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
5254
53− gupcast r = M1 (K1 (view (gkey @field) r))
55+ gupcast r = M1 (K1 (view (glens @(HasTotalFieldPSym field)) r))
5456
5557instance GUpcast sub sup => GUpcast sub (C1 c sup) where
5658 gupcast = M1 . gupcast
@@ -84,9 +86,12 @@ class GSmashLeaf sub sup (w :: Bool) where
8486 gsmashLeaf :: sup p -> sub p -> sub p
8587
8688instance
87− GHasKey field sup sup t t
89+ GLens' (HasTotalFieldPSym field) sup t
8890 => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
89− gsmashLeaf sup _ = M1 (K1 (view (gkey @field) sup))
91+ gsmashLeaf sup _ = M1 (K1 (view (glens @(HasTotalFieldPSym field)) sup))
9092
9193instance GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'False where
9294 gsmashLeaf _ = id
95+
96+data HasTotalFieldPSym :: Symbol -> (TyFun (Type -> Type) Bool)
97+type instance Eval (HasTotalFieldPSym sym) tt = HasTotalFieldP sym tt
src/Data/Generics/Product/Positions.hs+28−8
@@ -1,3 +1,5 @@
1+{-# LANGUAGE TypeInType #-}
2+{-# LANGUAGE PartialTypeSignatures #-}
13{-# LANGUAGE CPP #-}
24{-# LANGUAGE AllowAmbiguousTypes #-}
35{-# LANGUAGE ConstraintKinds #-}
@@ -42,12 +44,14 @@ import Data.Generics.Internal.VL.Lens as VL
4244import Data.Generics.Internal.Void
4345import Data.Generics.Internal.Families
4446import Data.Generics.Product.Internal.Positions
47+import Data.Generics.Product.Internal.GLens
4548
4649import Data.Kind (Constraint, Type)
4750import Data.Type.Bool (type (&&))
4851import GHC.Generics
4952import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
50−import Data.Generics.Internal.Profunctor.Lens
53+import Data.Generics.Internal.Profunctor.Lens as P
54+import Data.Coerce
5155
5256-- $setup
5357-- == /Running example:/
@@ -101,9 +105,11 @@ setPosition = VL.set (position' @i)
101105instance
102106 ( Generic s
103107 , ErrorUnless i s (0 <? i && i <=? Size (Rep s))
104− , GHasPosition' i (Rep s) a
108+ , cs ~ CRep s
109+ , Coercible (Rep s) cs
110+ , GLens' (HasTotalPositionPSym i) cs a
105111 ) => HasPosition' i s a where
106− position' f s = VL.ravel (repLens . gposition @i) f s
112+ position' f s = VL.ravel (repLens . coerced @cs @cs . glens @(HasTotalPositionPSym i)) f s
107113 {-# INLINE position' #-}
108114
109115instance -- see Note [Changing type parameters]
@@ -119,17 +125,28 @@ instance -- see Note [Changing type parameters]
119125#endif
120126 , Generic s'
121127 , Generic t'
122− , GHasPosition' i (Rep s) a
123− , GHasPosition' i (Rep s') a'
124− , GHasPosition i (Rep s) (Rep t) a b
128+ , GLens (HasTotalPositionPSym i) cs ct a b
129+ , cs ~ CRep s
130+ , ct ~ CRep t
131+ , GLens' (HasTotalPositionPSym i) (CRep s') a'
132+ , GLens' (HasTotalPositionPSym i) (CRep t') b'
125133 , t ~ Infer s a' b
126− , GHasPosition' i (Rep t') b'
127134 , s ~ Infer t b' a
135+ , Coercible cs (Rep s)
136+ , Coercible ct (Rep t)
128137 ) => HasPosition i s t a b where
129138
130− position = VL.ravel (repLens . gposition @i)
139+ position = VL.ravel (repLens . coerced @cs @ct . glens @(HasTotalPositionPSym i))
131140 {-# INLINE position #-}
132141
142+-- We wouldn't need the universal 'x' here if we could express above that
143+-- forall x. Coercible (cs x) (Rep s x), but this requires quantified
144+-- constraints
145+coerced :: forall s t s' t' x a b. (Coercible t t', Coercible s s')
146+ => P.ALens a b (s x) (t x) -> P.ALens a b (s' x) (t' x)
147+coerced = coerce
148+{-# INLINE coerced #-}
149+
133150-- See Note [Uncluttering type signatures]
134151instance {-# OVERLAPPING #-} HasPosition f (Void1 a) (Void1 b) a b where
135152 position = undefined
@@ -145,3 +162,6 @@ type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint wher
145162
146163 ErrorUnless _ _ 'True
147164 = ()
165+
166+data HasTotalPositionPSym :: Nat -> (TyFun (Type -> Type) Bool)
167+type instance Eval (HasTotalPositionPSym t) tt = HasTotalPositionP t tt
src/Data/Generics/Product/Typed.hs+8−3
@@ -7,6 +7,7 @@
77{-# LANGUAGE ScopedTypeVariables #-}
88{-# LANGUAGE TypeApplications #-}
99{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE TypeInType #-}
1011{-# LANGUAGE TypeOperators #-}
1112{-# LANGUAGE UndecidableInstances #-}
1213
@@ -33,7 +34,7 @@ module Data.Generics.Product.Typed
3334import Data.Generics.Internal.Families
3435import Data.Generics.Internal.VL.Lens as VL
3536import Data.Generics.Internal.Void
36−import Data.Generics.Product.Internal.Keyed
37+import Data.Generics.Product.Internal.GLens
3738
3839import Data.Kind (Constraint, Type)
3940import GHC.Generics (Generic (Rep))
@@ -110,10 +111,10 @@ class HasType a s where
110111instance
111112 ( Generic s
112113 , ErrorUnlessOne a s (CollectTotalType a (Rep s))
113− , GHasKey a (Rep s) (Rep s) a a
114+ , GLens (HasTotalTypePSym a) (Rep s) (Rep s) a a
114115 ) => HasType a s where
115116
116− typed f s = VL.ravel (repLens . gkey @a) f s
117+ typed f s = VL.ravel (repLens . glens @(HasTotalTypePSym a)) f s
117118
118119-- See Note [Uncluttering type signatures]
119120instance {-# OVERLAPPING #-} HasType a Void where
@@ -150,3 +151,7 @@ type family ErrorUnlessOne (a :: Type) (s :: Type) (stat :: TypeStat) :: Constra
150151
151152 ErrorUnlessOne _ _ ('TypeStat '[] '[] _)
152153 = ()
154+
155+data HasTotalTypePSym :: Type -> (TyFun (Type -> Type) Bool)
156+type instance Eval (HasTotalTypePSym t) tt = HasTotalTypeP t tt
157+