on master help sign in
Add constrained traversals and multitraversals

Simple "typed" traversals are now reimplemented using the constrained version. Multitraversals do not yet have a user-facing API

commit95d451a096c22e52380279a0c7b6d4842381119d authorCsongor Kiss <kiss.csongor.kiss@gmail.com> dateSun 4 Feb 2018, 00:36 (9 years ago) parenta184835
7 files changed+154−52
generic-lens.cabal+1 src/Data/Generics/Internal/VL/Iso.hs+3 src/Data/Generics/Internal/VL/Traversal.hs+21−7 src/Data/Generics/Product/Constraints.hs+45 src/Data/Generics/Product/Internal/Types.hs+75−34 src/Data/Generics/Product/Types.hs+4−10 test/Spec.hs+5−1
generic-lens.cabal+1
@@ -30,6 +30,7 @@ library
3030 , Data.Generics.Product.Subtype
3131 , Data.Generics.Product.Typed
3232 , Data.Generics.Product.Types
33+ , Data.Generics.Product.Constraints
3334 , Data.Generics.Product.List
3435
3536 , Data.Generics.Sum
src/Data/Generics/Internal/VL/Iso.hs+3
@@ -37,6 +37,9 @@ repIso = iso from to
3737mIso :: Iso (M1 i c f p) (M1 i c g p) (f p) (g p)
3838mIso = iso unM1 M1
3939
40+kIso :: Iso (K1 r a p) (K1 r b p) a b
41+kIso = iso unK1 K1
42+
4043prodIso :: Iso ((a :*: b) x) ((a' :*: b') x) (a x, b x) (a' x, b' x)
4144prodIso = iso (\(a :*: b) -> (a, b)) (\(a, b) -> (a :*: b))
4245
src/Data/Generics/Internal/VL/Traversal.hs+21−7
@@ -1,3 +1,5 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
13{-# LANGUAGE GADTs #-}
24{-# LANGUAGE NoMonomorphismRestriction #-}
35{-# LANGUAGE Rank2Types #-}
@@ -24,20 +26,32 @@ module Data.Generics.Internal.VL.Traversal where
2426type Traversal' s a
2527 = forall f. Applicative f => (a -> f a) -> s -> f s
2628
29+type TraversalC' s c
30+ = forall f. Applicative f => LensLikeC f s c
31+
32+type LensLikeC f s c
33+ = (forall a. c a => a -> f a) -> s -> f s
34+
2735type Traversal s t a b
2836 = forall f. Applicative f => (a -> f b) -> s -> f t
2937
3038confusing :: Applicative f => Traversal s t a b -> (a -> f b) -> s -> f t
3139confusing t = \f -> lowerYoneda . lowerCurried . t (liftCurriedYoneda . f)
32− where
33− liftCurriedYoneda :: Applicative f => f a -> Curried (Yoneda f) a
34− liftCurriedYoneda fa = Curried (`yap` fa)
35− {-# INLINE liftCurriedYoneda #-}
36− yap :: Applicative f => Yoneda f (a -> b) -> f a -> Yoneda f b
37− yap (Yoneda k) fa = Yoneda (\ab_r -> k (ab_r .) <*> fa)
38− {-# INLINE yap #-}
3940{-# INLINE confusing #-}
4041
42+-- fuse constrained traversals
43+confusingC :: forall c f s. Applicative f => TraversalC' s c -> LensLikeC f s c
44+confusingC t = \f -> lowerYoneda . lowerCurried . t (liftCurriedYoneda . f)
45+{-# INLINE confusingC #-}
46+
47+liftCurriedYoneda :: Applicative f => f a -> Curried (Yoneda f) a
48+liftCurriedYoneda fa = Curried (`yap` fa)
49+{-# INLINE liftCurriedYoneda #-}
50+
51+yap :: Applicative f => Yoneda f (a -> b) -> f a -> Yoneda f b
52+yap (Yoneda k) fa = Yoneda (\ab_r -> k (ab_r .) <*> fa)
53+{-# INLINE yap #-}
54+
4155newtype Curried f a =
4256 Curried { runCurried :: forall r. f (a -> r) -> f r }
4357
src/Data/Generics/Product/Constraints.hsnew file+45
@@ -0,0 +1,45 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE Rank2Types #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeApplications #-}
9+{-# LANGUAGE UndecidableInstances #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Product.Constraints
14+-- Copyright : (C) 2018 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Constrained traversals.
21+--
22+-----------------------------------------------------------------------------
23+
24+module Data.Generics.Product.Constraints
25+ ( -- *Traversals
26+ --
27+ -- $example
28+ HasConstraints (..)
29+ ) where
30+
31+import Data.Generics.Product.Internal.Types
32+import Data.Kind (Constraint)
33+
34+import GHC.Generics (Generic (Rep), from, to)
35+import Data.Generics.Internal.VL.Traversal
36+
37+class HasConstraints (c :: * -> Constraint) s where
38+ constraints :: Applicative g => (forall a. c a => a -> g a) -> s -> g s
39+
40+instance
41+ ( Generic s
42+ , GHasConstraints c (Rep s)
43+ ) => HasConstraints c s where
44+ constraints = confusingC @c (\f s -> to <$> cgtypes @c f (from s))
45+ {-# INLINE constraints #-}
src/Data/Generics/Product/Internal/Types.hs+75−34
@@ -1,13 +1,20 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE TypeOperators #-}
10−{-# LANGUAGE UndecidableInstances #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE GADTs #-}
8+{-# LANGUAGE KindSignatures #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE Rank2Types #-}
12+{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE TypeApplications #-}
14+{-# LANGUAGE TypeFamilies #-}
15+{-# LANGUAGE TypeFamilyDependencies #-}
16+{-# LANGUAGE TypeOperators #-}
17+{-# LANGUAGE UndecidableInstances #-}
1118
1219-----------------------------------------------------------------------------
1320-- |
@@ -23,37 +30,71 @@
2330-----------------------------------------------------------------------------
2431
2532module Data.Generics.Product.Internal.Types
26− ( GHasTypes (..)
33+ ( GHasTypes
34+ , GHasConstraints (..)
35+ , gtypes
36+ , HList (..)
2737 ) where
2838
29−import Data.Kind (Type)
39+import Data.Kind (Type, Constraint)
3040import GHC.Generics
41+import Data.Generics.Internal.VL.Iso
3142
32−-- |As 'HasTypes' but over generic representations as defined by
33−-- "GHC.Generics".
34−class GHasTypes (f :: Type -> Type) a where
35− gtypes :: forall g x. Applicative g => (a -> g a) -> f x -> g (f x)
43+-- | Constrained traversal.
44+class GHasConstraints (c :: * -> Constraint) (f :: * -> *) where
45+ cgtypes :: forall g x.
46+ Applicative g => (forall a. c a => a -> g a) -> f x -> g (f x)
3647
37−instance (GHasTypes l a, GHasTypes r a) => GHasTypes (l :*: r) a where
38− gtypes f (l :*: r) = (:*:) <$> gtypes f l <*> gtypes f r
39− {-# INLINE gtypes #-}
48+instance (GHasConstraints c l, GHasConstraints c r) => GHasConstraints c (l :*: r) where
49+ cgtypes f (l :*: r) = (:*:) <$> cgtypes @c f l <*> cgtypes @c f r
4050
41−instance (GHasTypes l a, GHasTypes r a) => GHasTypes (l :+: r) a where
42− gtypes f (L1 l) = L1 <$> gtypes f l
43− gtypes f (R1 r) = R1 <$> gtypes f r
51+instance (GHasConstraints c l, GHasConstraints c r) => GHasConstraints c (l :+: r) where
52+ cgtypes f (L1 l) = L1 <$> cgtypes @c f l
53+ cgtypes f (R1 r) = R1 <$> cgtypes @c f r
4454
45−instance GHasTypes (K1 R a) a where
46− gtypes f (K1 x) = fmap K1 (f x)
47− {-# INLINE gtypes #-}
55+instance c a => GHasConstraints c (K1 R a) where
56+ cgtypes = kIso
4857
49−instance {-# OVERLAPPABLE #-} GHasTypes (K1 R a) b where
50− gtypes _ k = pure k
51− {-# INLINE gtypes #-}
58+instance GHasConstraints c f => GHasConstraints c (M1 m meta f) where
59+ cgtypes f (M1 x) = M1 <$> cgtypes @c f x
5260
53−instance GHasTypes f a => GHasTypes (M1 m meta f) a where
54− gtypes f (M1 x) = M1 <$> gtypes f x
55− {-# INLINE gtypes #-}
61+instance GHasConstraints c U1 where
62+ cgtypes _ _ = pure U1
5663
57−instance GHasTypes U1 a where
58− gtypes _ U1 = pure U1
59− {-# INLINE gtypes #-}
64+--------------------------------------------------------------------------------
65+
66+-- | Multi-traversal
67+type GHasTypes rep as = GHasConstraints (Contains as) rep
68+
69+-- Try to use a function from the list, or default to 'pure' if not present
70+gtypes
71+ :: forall as x f g.
72+ ( GHasTypes f as
73+ , Applicative g
74+ ) => HList (Functions as g) -> f x -> g (f x)
75+gtypes hl s = cgtypes @(Contains as) (pick hl) s
76+
77+--------------------------------------------------------------------------------
78+
79+data HList (ts :: [Type]) where
80+ HNil :: HList '[]
81+ (:>) :: a -> HList as -> HList (a ': as)
82+infixr 5 :>
83+
84+-- >>> :kind! Functions '[Int, Char, Bool] Maybe
85+-- '[Int -> Maybe Int, Char -> Maybe Char, Bool -> Maybe Bool]
86+type family Functions (ts :: [Type]) (g :: Type -> Type) = r | r -> ts where
87+ Functions '[] _ = '[]
88+ Functions (t ': ts) g = ((t -> g t) ': Functions ts g)
89+
90+class Contains as a where
91+ pick :: Applicative g => HList (Functions as g) -> a -> g a
92+
93+instance {-# OVERLAPPING #-} Contains (a ': as) a where
94+ pick (h :> _) = h
95+
96+instance Contains as a => Contains (b ': as) a where
97+ pick (_ :> hs) = pick hs
98+
99+instance Contains '[] a where
100+ pick _ = pure
src/Data/Generics/Product/Types.hs+4−10
@@ -1,11 +1,6 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
21{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
42{-# LANGUAGE FlexibleInstances #-}
53{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE TypeOperators #-}
94{-# LANGUAGE UndecidableInstances #-}
105
116-----------------------------------------------------------------------------
@@ -30,17 +25,16 @@ module Data.Generics.Product.Types
3025
3126import Data.Generics.Product.Internal.Types
3227
33−import GHC.Generics (Generic (Rep), from, to)
28+import GHC.Generics (Generic (Rep))
3429import Data.Generics.Internal.VL.Traversal
30+import Data.Generics.Internal.VL.Iso
3531
3632class HasTypes a s where
3733 types :: Traversal' s a
3834
3935instance
4036 ( Generic s
41− , GHasTypes (Rep s) a
37+ , GHasTypes (Rep s) '[a]
4238 ) => HasTypes a s where
43−
44− types = confusing (\f s -> to <$> gtypes f (from s))
39+ types = confusing (\f -> (repIso . gtypes) (f :> HNil))
4540 {-# INLINE types #-}
46−
test/Spec.hs+5−1
@@ -202,4 +202,8 @@ tests = TestList $ map mkHUnitTest
202202 , $(inspectTest $ 'sum2PrismManual === 'sum2TypePrism)
203203 , $(inspectTest $ 'sum1PrismManualChar === 'sum1TypePrismChar)
204204 , $(inspectTest $ 'sum2PrismManualChar === 'sum2TypePrismChar)
205− , $(inspectTest $ 'sum1PrismManual === 'sum1TypePrism) ]
205+ , $(inspectTest $ 'sum1PrismManual === 'sum1TypePrism)
206+ , $(inspectTest $ 'intTraversalManual === 'intTraversalDerived)
207+ ]
208+
209+-- TODO: add test for traversals over multiple types