on master help sign in
Type-changing lenses
4e50855Csongor Kiss
README.md+18−2
@@ -1,7 +1,6 @@
1# generic-lens
1# generic-lens
2
2
3−[![Build
4−Status](https://travis-ci.org/kcsongor/generic-lens.svg?branch=master)](https://travis-ci.org/kcsongor/generic-lens)
3+[![Build Status](https://travis-ci.org/kcsongor/generic-lens.svg?branch=master)](https://travis-ci.org/kcsongor/generic-lens)
5[![Hackage](https://img.shields.io/hackage/v/generic-lens.svg)](https://hackage.haskell.org/package/generic-lens)
4[![Hackage](https://img.shields.io/hackage/v/generic-lens.svg)](https://hackage.haskell.org/package/generic-lens)
6
5
7Generically derive lenses and prisms for data types.
6Generically derive lenses and prisms for data types.
@@ -50,6 +49,23 @@ error:
50 • The type Person does not contain a field named "pet"
49 • The type Person does not contain a field named "pet"
51```
50```
52
51
52+If the accessed field is a type parameter that appears uniquely in the type,
53+then its type can be changed:
54+
55+```haskell
56+data T a b c d = T
57+ { paramA :: a
58+ , paramB :: b
59+ , paramC :: c
60+ , paramD :: d
61+ }
62+ deriving (Generic, Show)
63+
64+>>> t = T "a" (10 :: Int) 'c' False
65+>>> t & field @"paramA" .~ 'a' & field @"paramB" .~ False
66+T {paramA = 'a', paramB = False, paramC = 'c', paramD = False}
67+```
68+
53### Positional fields
69### Positional fields
54
70
55Fields can be accessed by their position in the data structure (index starting at 1):
71Fields can be accessed by their position in the data structure (index starting at 1):
examples/Examples.hs+16−1
@@ -11,7 +11,7 @@ module Examples where
11
11
12import GHC.Generics
12import GHC.Generics
13import Data.Generics.Product
13import Data.Generics.Product
14−--import Control.Lens
14+import Control.Lens
15
15
16data Animal = Animal
16data Animal = Animal
17 { name :: String
17 { name :: String
@@ -41,3 +41,18 @@ data MyRecord = MyRecord { field1 :: Int, field2 :: String } deriving Generic
41
41
42--g :: Subtype s MyRecord => s -> String
42--g :: Subtype s MyRecord => s -> String
43--g s = s ^. super @MyRecord . label @"field2"
43--g s = s ^. super @MyRecord . label @"field2"
44+
45+data Test a b = Test { fieldInt :: Int, fieldA :: a, fieldB :: b } deriving (Generic, Show)
46+
47+-- changedA :: Test Int String
48+-- >>> changedA
49+-- Test {fieldInt = 10, fieldA = 10, fieldB = "world"}
50+changedA = set (field @"fieldA") (10 :: Int) (Test 10 "hello" "world")
51+
52+-- changedB :: Test String Int
53+-- >>> changedB
54+-- Test {fieldInt = 10, fieldA = "hello", fieldB = 10}
55+changedB = set (field @"fieldB") (10 :: Int) (Test 10 "hello" "world")
56+
57+--changedInt = set (field @"fieldInt") ("hello") (Test 10 "hello" "world")
58+-- type error
generic-lens.cabal+12−1
@@ -1,5 +1,5 @@
1name: generic-lens
1name: generic-lens
2−version: 0.4.1.1
2+version: 0.5.0.0
3synopsis: Generic data-structure operations exposed as lenses.
3synopsis: Generic data-structure operations exposed as lenses.
4description: This package uses the GHC 8 Generic representation to derive various operations on data structures with a lens interface, including structural subtype relationship between records and positional indexing into arbitrary product types.
4description: This package uses the GHC 8 Generic representation to derive various operations on data structures with a lens interface, including structural subtype relationship between records and positional indexing into arbitrary product types.
5
5
@@ -34,9 +34,11 @@ library
34 , Data.Generics.Internal.Lens
34 , Data.Generics.Internal.Lens
35
35
36 other-modules: Data.Generics.Internal.Families
36 other-modules: Data.Generics.Internal.Families
37+ , Data.Generics.Internal.Families.Changing
37 , Data.Generics.Internal.Families.Count
38 , Data.Generics.Internal.Families.Count
38 , Data.Generics.Internal.Families.Has
39 , Data.Generics.Internal.Families.Has
39 , Data.Generics.Internal.HList
40 , Data.Generics.Internal.HList
41+ , Data.Generics.Internal.Void
40
42
41 , Data.Generics.Sum.Internal.Constructors
43 , Data.Generics.Sum.Internal.Constructors
42 , Data.Generics.Sum.Internal.Typed
44 , Data.Generics.Sum.Internal.Typed
@@ -53,6 +55,7 @@ library
53
55
54 build-depends: base >= 4.9 && <= 5.0
56 build-depends: base >= 4.9 && <= 5.0
55 , profunctors >= 5.0 && <= 6.0
57 , profunctors >= 5.0 && <= 6.0
58+ , tagged >= 0.8 && <= 0.9
56
59
57 hs-source-dirs: src
60 hs-source-dirs: src
58 default-language: Haskell2010
61 default-language: Haskell2010
@@ -74,6 +77,14 @@ test-suite generic-lens-test
74 default-language: Haskell2010
77 default-language: Haskell2010
75 ghc-options: -Wall
78 ghc-options: -Wall
76
79
80+test-suite doctests
81+ default-language: Haskell2010
82+ type: exitcode-stdio-1.0
83+ ghc-options: -threaded
84+ main-is: doctest.hs
85+ build-depends: base >4 && <5, doctest
86+ hs-source-dirs: test
87+
77benchmark generic-lens-bench
88benchmark generic-lens-bench
78 type: exitcode-stdio-1.0
89 type: exitcode-stdio-1.0
79 hs-source-dirs: benchmarks
90 hs-source-dirs: benchmarks
src/Data/Generics/Internal/Families.hs+3−2
@@ -12,5 +12,6 @@ module Data.Generics.Internal.Families
12 ( module Families
12 ( module Families
13 ) where
13 ) where
14
14
15−import Data.Generics.Internal.Families.Count as Families
15+import Data.Generics.Internal.Families.Count as Families
16−import Data.Generics.Internal.Families.Has as Families
16+import Data.Generics.Internal.Families.Has as Families
17+import Data.Generics.Internal.Families.Changing as Families
src/Data/Generics/Internal/Families/Changing.hsnew file+58
@@ -0,0 +1,58 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE PolyKinds #-}
3+{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE TypeOperators #-}
5+{-# LANGUAGE UndecidableInstances #-}
6+
7+module Data.Generics.Internal.Families.Changing where
8+
9+import GHC.TypeLits (Nat, type (+))
10+
11+{-
12+ Note [Changing type parameters]
13+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14+
15+ To get good type inference for type-changing lenses, we want to be able
16+ map the field's type back to the type argument it corresponds to. This way,
17+ when the field is changed, we know what the result type of the structure is
18+ going to be.
19+
20+ However, for a given type @t@, its representation @Rep t@ forgets which types
21+ in the structure came from type variables, and which didn't. An @Int@ that
22+ results from the instantiation of the type paremeter and an @Int@ that was
23+ monomorphically specified in the structure are indistinguishable.
24+
25+ The solution is to replace the type arguments in the type with unique
26+ proxies, like: @T a b@ -> @T (P 1 a) (P 0 b)@. This way, if looking up
27+ a field's type yields something of shape @P _ _@, we know it came from a type
28+ parameter, and also know which.
29+
30+ If the field's type is a proxy, then its type is allowed to change, otherwise
31+ not. This also allows us to satisfy the functional dependency @s field b -> t@.
32+ If after doing the conversion on @s@, @field@'s type is @(P _ a), then @t@ is
33+ @s[b/a]@, otherwise @t ~ s@ and @b ~ a@.
34+-}
35+data P (i :: Nat) a
36+
37+type Proxied t = Proxied' t 0
38+
39+type family Proxied' (t :: k) (next :: Nat) :: k where
40+ Proxied' (t a :: k) next = (Proxied' t (next + 1)) (P next a)
41+ Proxied' t _ = t
42+
43+type family UnProxied (t :: k) :: k where
44+ UnProxied (t (P _ a) :: k) = UnProxied t a
45+ UnProxied t = t
46+
47+type family Change (t :: k) (target :: Nat) (to :: j) :: k where
48+ Change (t (P target _) :: k) target to = t (P target to)
49+ Change (t a :: k) target to = Change t target to a
50+ Change t _ _ = t
51+
52+type family IsParam a where
53+ IsParam (P _ _) = 'True
54+ IsParam _ = 'False
55+
56+type family IndexOf a where
57+ IndexOf (P i a) = i
58+
src/Data/Generics/Internal/Lens.hs+34−11
@@ -16,14 +16,20 @@
16-----------------------------------------------------------------------------
16-----------------------------------------------------------------------------
17module Data.Generics.Internal.Lens where
17module Data.Generics.Internal.Lens where
18
18
19−import Control.Applicative (Const(..))
19+import Control.Applicative (Const(..))
20−import Data.Functor.Identity (Identity(..))
20+import Data.Functor.Identity (Identity(..))
21+import Data.Monoid (First (..))
21−import Data.Profunctor (Choice(right'), Profunctor(dimap))
22+import Data.Profunctor (Choice(right'), Profunctor(dimap))
23+import Data.Profunctor.Unsafe ((#.), (.#))
24+import Data.Tagged
22−import GHC.Generics ((:*:)(..), (:+:)(..), Generic(..), M1(..), Rep)
25+import GHC.Generics ((:*:)(..), (:+:)(..), Generic(..), M1(..), Rep)
23
26
24-- | Type alias for lens
27-- | Type alias for lens
25type Lens' s a
28type Lens' s a
29+ = Lens s s a a
30+
31+type Lens s t a b
26− = forall f. Functor f => (a -> f a) -> s -> f s
32+ = forall f. Functor f => (a -> f b) -> s -> f t
27
33
28-- | Type alias for prism
34-- | Type alias for prism
29type Prism' s a
35type Prism' s a
@@ -32,6 +38,9 @@ type Prism' s a
32type Iso' s a
38type Iso' s a
33 = forall p f. (Profunctor p, Functor f) => p a (f a) -> p s (f s)
39 = forall p f. (Profunctor p, Functor f) => p a (f a) -> p s (f s)
34
40
41+type Iso s t a b
42+ = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t)
43+
35-- | Getting
44-- | Getting
36(^.) :: s -> ((a -> Const a a) -> s -> Const a s) -> a
45(^.) :: s -> ((a -> Const a a) -> s -> Const a s) -> a
37s ^. l = getConst (l Const s)
46s ^. l = getConst (l Const s)
@@ -42,13 +51,26 @@ set :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
42set l b
51set l b
43 = runIdentity . l (\_ -> Identity b)
52 = runIdentity . l (\_ -> Identity b)
44
53
54+infixr 4 .~
55+(.~) :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
56+(.~) = set
57+
58+infixl 8 ^?
59+(^?) :: s -> ((a -> Const (First a) a) -> s -> Const (First a) s) -> Maybe a
60+s ^? l = getFirst (fmof l (First #. Just) s)
61+ where fmof l' f = getConst #. l' (Const #. f)
62+
63+infixr 8 #
64+(#) :: (Tagged b (Identity b) -> Tagged t (Identity t)) -> b -> t
65+(#) p = runIdentity #. unTagged #. p .# Tagged .# Identity
66+
45-- | Lens focusing on the first element of a product
67-- | Lens focusing on the first element of a product
46−first :: Lens' ((a :*: b) x) (a x)
68+first :: Lens ((a :*: b) x) ((a' :*: b) x) (a x) (a' x)
47first f (a :*: b)
69first f (a :*: b)
48 = fmap (:*: b) (f a)
70 = fmap (:*: b) (f a)
49
71
50-- | Lens focusing on the second element of a product
72-- | Lens focusing on the second element of a product
51−second :: Lens' ((a :*: b) x) (b x)
73+second :: Lens ((a :*: b) x) ((a :*: b') x) (b x) (b' x)
52second f (a :*: b)
74second f (a :*: b)
53 = fmap (a :*:) (f b)
75 = fmap (a :*:) (f b)
54
76
@@ -70,19 +92,20 @@ prism :: (a -> s) -> (s -> Either s a) -> Prism' s a
70prism bt seta = dimap seta (either pure (fmap bt)) . right'
92prism bt seta = dimap seta (either pure (fmap bt)) . right'
71
93
72-- | A type and its generic representation are isomorphic
94-- | A type and its generic representation are isomorphic
73−repIso :: Generic a => Iso' a (Rep a x)
95+repIso :: (Generic a, Generic b) => Iso a b (Rep a x) (Rep b x)
74repIso = dimap from (fmap to)
96repIso = dimap from (fmap to)
75
97
76-- | 'M1' is just a wrapper around `f p`
98-- | 'M1' is just a wrapper around `f p`
77−mIso :: Iso' (M1 i c f p) (f p)
99+--mIso :: Iso' (M1 i c f p) (f p)
100+mIso :: Iso (M1 i c f p) (M1 i c g p) (f p) (g p)
78mIso = dimap unM1 (fmap M1)
101mIso = dimap unM1 (fmap M1)
79
102
80-- These are specialised versions of the Isos above. On GHC 8.0.2, having
103-- These are specialised versions of the Isos above. On GHC 8.0.2, having
81-- these functions eta-expanded allows the optimiser to inline these functions.
104-- these functions eta-expanded allows the optimiser to inline these functions.
82−mLens :: Lens' (M1 i c f p) (f p)
105+mLens :: Lens (M1 i c f p) (M1 i c g p) (f p) (g p)
83mLens f s = mIso f s
106mLens f s = mIso f s
84
107
85−repLens :: Generic a => Lens' a (Rep a x)
108+repLens :: (Generic a, Generic b) => Lens a b (Rep a x) (Rep b x)
86repLens f s = repIso f s
109repLens f s = repIso f s
87
110
88sumIso :: Iso' ((a :+: b) x) (Either (a x) (b x))
111sumIso :: Iso' ((a :+: b) x) (Either (a x) (b x))
src/Data/Generics/Internal/Void.hsnew file+16
@@ -0,0 +1,16 @@
1+module Data.Generics.Internal.Void where
2+
3+{-
4+ Note [Uncluttering type signatures]
5+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
7+ Because the various instances in the library always match (the Has* classes
8+ are essentially glorified constraint synonyms), they get replaced with
9+ their constraints, resulting in large, unreadable types.
10+
11+ Writing an (overlapping instance) for this Void type means that the original
12+ instance might not be the one selected, thus GHC leaves the constraints in
13+ place until further information is provided, at which point the type
14+ machinery has sufficient information to reduce to sensible types.
15+-}
16+data Void
src/Data/Generics/Product/Any.hs+23−23
@@ -6,6 +6,7 @@
6{-# LANGUAGE PolyKinds #-}
6{-# LANGUAGE PolyKinds #-}
7{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeApplications #-}
8{-# LANGUAGE TypeApplications #-}
9+{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE UndecidableInstances #-}
10{-# LANGUAGE UndecidableInstances #-}
10
11
11-----------------------------------------------------------------------------
12-----------------------------------------------------------------------------
@@ -24,7 +25,7 @@
24module Data.Generics.Product.Any
25module Data.Generics.Product.Any
25 ( -- *Lenses
26 ( -- *Lenses
26 --
27 --
27− -- $example
28+ -- $setup
28 HasAny (..)
29 HasAny (..)
29 ) where
30 ) where
30
31
@@ -33,26 +34,25 @@ import Data.Generics.Product.Fields
33import Data.Generics.Product.Positions
34import Data.Generics.Product.Positions
34import Data.Generics.Product.Typed
35import Data.Generics.Product.Typed
35
36
36−-- $example
37+-- $setup
37−-- @
38+-- >>> :set -XTypeApplications
38−-- module Example where
39+-- >>> :set -XDataKinds
39−--
40+-- >>> :set -XDeriveGeneric
40−-- import Data.Generics.Product
41−-- import GHC.Generics
41+-- >>> import GHC.Generics
42−--
42+-- >>> :m +Data.Generics.Internal.Lens
43+-- >>> :{
43−-- data Human = Human
44+-- data Human = Human
44−-- { name :: String
45+-- { name :: String
45−-- , age :: Int
46+-- , age :: Int
46−-- , address :: String
47+-- , address :: String
47−-- }
48+-- }
48−-- deriving (Generic, Show)
49+-- deriving (Generic, Show)
49−--
50−-- human :: Human
50+-- human :: Human
51−-- human = Human \"Tunyasz\" 50 \"London\"
51+-- human = Human "Tunyasz" 50 "London"
52−-- @
52+-- :}
53
53
54-- |Records that have generic lenses.
54-- |Records that have generic lenses.
55−class HasAny (sel :: k) a s | s sel k -> a where
55+class HasAny (sel :: k) s t a b | s sel k -> a, s sel k b -> t where
56 -- |A lens that focuses on a part of a product as identified by some
56 -- |A lens that focuses on a part of a product as identified by some
57 -- selector. Currently supported selectors are field names, positions and
57 -- selector. Currently supported selectors are field names, positions and
58 -- unique types. Compatible with the lens package's 'Control.Lens.Lens'
58 -- unique types. Compatible with the lens package's 'Control.Lens.Lens'
@@ -64,13 +64,13 @@ class HasAny (sel :: k) a s | s sel k -> a where
64 -- "Tunyasz"
64 -- "Tunyasz"
65 -- >>> human ^. the @3
65 -- >>> human ^. the @3
66 -- "London"
66 -- "London"
67− the :: Lens' s a
67+ the :: Lens s t a b
68
68
69−instance HasPosition i a s => HasAny i a s where
69+instance HasPosition i s t a b => HasAny i s t a b where
70 the = position @i
70 the = position @i
71
71
72−instance HasField field a s => HasAny field a s where
72+instance HasField field s t a b => HasAny field s t a b where
73 the = field @field
73 the = field @field
74
74
75−instance HasType a s => HasAny a a s where
75+instance (HasType a s, t ~ s, a ~ b) => HasAny a s t a b where
76 the = typed @a
76 the = typed @a
src/Data/Generics/Product/Fields.hs+76−52
@@ -1,14 +1,16 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
8+{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE TypeInType #-}
10−{-# LANGUAGE TypeOperators #-}
11+{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE UndecidableSuperClasses #-}
12
14
13-----------------------------------------------------------------------------
15-----------------------------------------------------------------------------
14-- |
16-- |
@@ -26,72 +28,94 @@
26module Data.Generics.Product.Fields
28module Data.Generics.Product.Fields
27 ( -- *Lenses
29 ( -- *Lenses
28
30
29− -- $example
31+ -- $setup
30 HasField (..)
32 HasField (..)
33+ , HasField'
34+
35+ , getField
36+ , setField
31 ) where
37 ) where
32
38
33import Data.Generics.Internal.Families
39import Data.Generics.Internal.Families
34import Data.Generics.Internal.Lens
40import Data.Generics.Internal.Lens
41+import Data.Generics.Internal.Void
35import Data.Generics.Product.Internal.Fields
42import Data.Generics.Product.Internal.Fields
36
43
37import Data.Kind (Constraint, Type)
44import Data.Kind (Constraint, Type)
38import GHC.Generics
45import GHC.Generics
39import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
46import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
47+import Data.Type.Bool (If)
40
48
41−-- $example
49+-- $setup
42−-- @
50+-- >>> :set -XTypeApplications
43−-- module Example where
51+-- >>> :set -XDataKinds
44−--
52+-- >>> :set -XDeriveGeneric
45−-- import Data.Generics.Product
53+-- >>> :set -XGADTs
54+-- >>> :set -XFlexibleContexts
46−-- import GHC.Generics
55+-- >>> import GHC.Generics
47−--
56+-- >>> :m +Data.Generics.Internal.Lens
57+-- >>> :m +Data.Function
58+-- >>> :{
48−-- data Human = Human
59+-- data Human a = Human
49−-- { name :: String
60+-- { name :: String
50−-- , age :: Int
61+-- , age :: Int
51−-- , address :: String
62+-- , address :: String
52−-- }
63+-- , other :: a
64+-- }
53−-- deriving (Generic, Show)
65+-- deriving (Generic, Show)
54−--
55−-- human :: Human
66+-- human :: Human Bool
56−-- human = Human \"Tunyasz\" 50 \"London\"
67+-- human = Human { name = "Tunyasz", age = 50, address = "London", other = False }
57−-- @
68+-- :}
58
69
59-- |Records that have a field with a given name.
70-- |Records that have a field with a given name.
60−class HasField (field :: Symbol) a s | s field -> a where
71+class HasField (field :: Symbol) s t a b | s field b -> t, s field -> a where
61 -- |A lens that focuses on a field with a given name. Compatible with the
72 -- |A lens that focuses on a field with a given name. Compatible with the
62 -- lens package's 'Control.Lens.Lens' type.
73 -- lens package's 'Control.Lens.Lens' type.
63 --
74 --
64 -- >>> human ^. field @"age"
75 -- >>> human ^. field @"age"
65 -- 50
76 -- 50
66− -- >>> human & field @"name" .~ "Tamas"
67− -- Human {name = "Tamas", age = 50, address = "London"}
68− field :: Lens' s a
69− field f s
70− = fmap (flip (setField @field) s) (f (getField @field s))
71−
72− -- |Get 'field'
73 --
77 --
74− -- >>> getField @"name" human
78+ -- If the field's type comes from a type parameter, it can be changed:
75− -- "Tunyasz"
79+ -- >>> :t human
76− getField :: s -> a
80+ -- human :: Human Bool
77− getField s = s ^. field @field
81+ -- >>> :t human & field @"other" .~ 42
82+ -- human & field @"other" .~ 42 :: Num b => Human b
83+ -- >>> human & field @"other" .~ 42
84+ -- Human {name = "Tunyasz", age = 50, address = "London", other = 42}
85+ field :: Lens s t a b
78
86
79− -- |Set 'field'
87+type HasField' field s a = HasField field s s a a
80− --
81− -- >>> setField @"age" (setField @"name" "Tamas" human) 30
82− -- Human {name = "Tamas", age = 30, address = "London"}
83− setField :: a -> s -> s
84− setField = set (field @field)
85
88
86− {-# MINIMAL field | setField, getField #-}
89+-- |
90+-- >>> getField @"age" human
91+-- 50
92+getField :: forall f s a. HasField' f s a => s -> a
93+getField s = s ^. field @f
87
94
88−instance
95+-- |
96+-- >>> setField @"age" 60 human
97+-- Human {name = "Tunyasz", age = 60, address = "London", other = False}
98+setField :: forall f s a. HasField' f s a => a -> s -> s
99+setField = set (field @f)
100+
101+instance -- see Note [Changing type parameters]
89 ( Generic s
102 ( Generic s
90 , ErrorUnless field s (HasTotalFieldP field (Rep s))
103 , ErrorUnless field s (HasTotalFieldP field (Rep s))
104+ , Generic t
105+ , s' ~ Proxied s
106+ , Generic s'
107+ , GHasField' field (Rep s) a
108+ , GHasField' field (Rep s') a'
91− , GHasField field (Rep s) a
109+ , GHasField field (Rep s) (Rep t) a b
110+ , '(t', b') ~ If (IsParam a') '(Change s' (IndexOf a') b, P (IndexOf a') b) '(s', b)
111+ , t ~ UnProxied t'
92− ) => HasField field a s where
112+ ) => HasField field s t a b where
113+
114+ field f s = ravel (repLens . gfield @field) f s
93
115
94− field = ravel (repLens . gfield @field)
116+-- See Note [Uncluttering type signatures]
117+instance {-# OVERLAPPING #-} HasField f Void Void Void Void where
118+ field = undefined
95
119
96type family ErrorUnless (field :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
120type family ErrorUnless (field :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
97 ErrorUnless field s 'False
121 ErrorUnless field s 'False
src/Data/Generics/Product/Internal/Fields.hs+22−16
@@ -1,9 +1,11 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE Rank2Types #-}
7{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeApplications #-}
10{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeFamilies #-}
11{-# LANGUAGE TypeFamilies #-}
@@ -25,6 +27,7 @@
25
27
26module Data.Generics.Product.Internal.Fields
28module Data.Generics.Product.Internal.Fields
27 ( GHasField (..)
29 ( GHasField (..)
30+ , GHasField'
28 ) where
31 ) where
29
32
30import Data.Generics.Internal.Families
33import Data.Generics.Internal.Families
@@ -36,36 +39,39 @@ import GHC.TypeLits (Symbol)
36
39
37-- |As 'HasField' but over generic representations as defined by
40-- |As 'HasField' but over generic representations as defined by
38-- "GHC.Generics".
41-- "GHC.Generics".
39−class GHasField (field :: Symbol) (f :: Type -> Type) a | field f -> a where
42+class GHasField (field :: Symbol) (s :: Type -> Type) (t :: Type -> Type) a b | s field -> a, t field -> b where
40− gfield :: Lens' (f x) a
43+ gfield :: Lens (s x) (t x) a b
41
44
42−instance GProductHasField field l r a (HasTotalFieldP field l)
43− => GHasField field (l :*: r) a where
45+type GHasField' field s a = GHasField field s s a a
44
46
45− gfield = gproductField @field @_ @_ @_ @(HasTotalFieldP field l)
47+instance GProductHasField field l r l' r' a b (HasTotalFieldP field l)
48+ => GHasField field (l :*: r) (l' :*: r') a b where
46
49
47−instance (GHasField field l a, GHasField field r a)
50+ gfield = gproductField @field @_ @_ @_ @_ @_ @_ @(HasTotalFieldP field l)
48− => GHasField field (l :+: r) a where
49
51
50− gfield = combine (gfield @field @l) (gfield @field @r)
52+instance (GHasField field r r a a, GHasField field l l a a, GHasField field l l' a b, GHasField field r r' a b)
53+ => GHasField field (l :+: r) (l' :+: r') a b where
51
54
55+ gfield f (L1 s) = fmap (\a -> L1 (set (gfield @field) a s)) (f (s ^. gfield @field))
56+ gfield f (R1 t) = fmap (\a -> R1 (set (gfield @field) a t)) (f (t ^. gfield @field))
57+
52−instance GHasField field (K1 R a) a where
58+instance GHasField field (K1 R a) (K1 R b) a b where
53 gfield f (K1 x) = fmap K1 (f x)
59 gfield f (K1 x) = fmap K1 (f x)
54
60
55−instance GHasField field (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 a)) a where
61+instance GHasField field (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 a)) (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 b)) a b where
56 gfield = mLens . gfield @field
62 gfield = mLens . gfield @field
57
63
58−instance GHasField field f a => GHasField field (M1 D meta f) a where
64+instance (Functor g, GHasField field f g a b) => GHasField field (M1 D meta f) (M1 D meta g) a b where
59 gfield = mLens . gfield @field
65 gfield = mLens . gfield @field
60
66
61−instance GHasField field f a => GHasField field (M1 C meta f) a where
67+instance GHasField field f g a b => GHasField field (M1 C meta f) (M1 C meta g) a b where
62 gfield = mLens . gfield @field
68 gfield = mLens . gfield @field
63
69
64−class GProductHasField (field :: Symbol) l r a (left :: Bool) | left field l r -> a where
70+class GProductHasField (field :: Symbol) l r l' r' a b (left :: Bool) | field l r -> a, field l' r' -> b where
65− gproductField :: Lens' ((l :*: r) x) a
71+ gproductField :: Lens ((l :*: r) x) ((l' :*: r') x) a b
66
72
67−instance GHasField field l a => GProductHasField field l r a 'True where
73+instance GHasField field l l' a b => GProductHasField field l r l' r a b 'True where
68 gproductField = first . gfield @field
74 gproductField = first . gfield @field
69
75
70−instance GHasField field r a => GProductHasField field l r a 'False where
76+instance GHasField field r r' a b => GProductHasField field l r l r' a b 'False where
71 gproductField = second . gfield @field
77 gproductField = second . gfield @field
src/Data/Generics/Product/Internal/Positions.hs+16−12
@@ -1,5 +1,7 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE KindSignatures #-}
7{-# LANGUAGE KindSignatures #-}
@@ -26,6 +28,7 @@
26
28
27module Data.Generics.Product.Internal.Positions
29module Data.Generics.Product.Internal.Positions
28 ( GHasPosition (..)
30 ( GHasPosition (..)
31+ , GHasPosition'
29 , type (<?)
32 , type (<?)
30 , Size
33 , Size
31 ) where
34 ) where
@@ -39,31 +42,32 @@ import GHC.TypeLits (type (<=?), type (+), Nat)
39
42
40-- |As 'HasPosition' but over generic representations as defined by
43-- |As 'HasPosition' but over generic representations as defined by
41-- "GHC.Generics".
44-- "GHC.Generics".
42−class GHasPosition (offset :: Nat) (i :: Nat) (f :: Type -> Type) a | offset i f -> a where
45+class GHasPosition (offset :: Nat) (i :: Nat) (s :: Type -> Type) (t :: Type -> Type) a b | s offset i -> a, s t offset i -> b where
43− gposition :: Lens' (f x) a
46+ gposition :: Lens (s x) (t x) a b
44
47
48+type GHasPosition' i s a = GHasPosition 1 i s s a a
49+
45−instance GHasPosition i i (K1 R a) a where
50+instance GHasPosition i i (K1 R a) (K1 R b) a b where
46 gposition f (K1 x) = fmap K1 (f x)
51 gposition f (K1 x) = fmap K1 (f x)
47
52
48−instance GHasPosition offset i f a => GHasPosition offset i (M1 m meta f) a where
53+instance GHasPosition offset i s t a b => GHasPosition offset i (M1 m meta s) (M1 m meta t) a b where
49 gposition = mLens . gposition @offset @i
54 gposition = mLens . gposition @offset @i
50
55
51instance
56instance
52 ( goLeft ~ (i <? (offset + Size l))
57 ( goLeft ~ (i <? (offset + Size l))
53 , offset' ~ (If goLeft offset (offset + Size l))
58 , offset' ~ (If goLeft offset (offset + Size l))
54− , GProductHasPosition offset' i l r a goLeft
59+ , GProductHasPosition offset' i l r l' r' a b goLeft
55− ) => GHasPosition offset i (l :*: r) a where
60+ ) => GHasPosition offset i (l :*: r) (l' :*: r') a b where
56−
57− gposition = gproductPosition @offset' @i @_ @_ @_ @goLeft
58
61
62+ gposition = gproductPosition @offset' @i @_ @_ @_ @_ @_ @_ @goLeft
59
63
60−class GProductHasPosition (offset :: Nat) (i :: Nat) l r a (left :: Bool) | offset i l r left -> a where
64+class GProductHasPosition (offset :: Nat) (i :: Nat) l r l' r' a b (left :: Bool) | offset i l r -> a, offset i l r l' r' -> b where
61− gproductPosition :: Lens' ((l :*: r) x) a
65+ gproductPosition :: Lens ((l :*: r) x) ((l' :*: r') x) a b
62
66
63−instance GHasPosition offset i l a => GProductHasPosition offset i l r a 'True where
67+instance GHasPosition offset i l l' a b => GProductHasPosition offset i l r l' r a b 'True where
64 gproductPosition = first . gposition @offset @i
68 gproductPosition = first . gposition @offset @i
65
69
66−instance GHasPosition offset i r a => GProductHasPosition offset i l r a 'False where
70+instance GHasPosition offset i r r' a b => GProductHasPosition offset i l r l r' a b 'False where
67 gproductPosition = second . gposition @offset @i
71 gproductPosition = second . gposition @offset @i
68
72
69type family Size f :: Nat where
73type family Size f :: Nat where
src/Data/Generics/Product/Internal/Subtype.hs+2−2
@@ -47,7 +47,7 @@ instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
47 gupcast rep = gupcast rep :*: gupcast rep
47 gupcast rep = gupcast rep :*: gupcast rep
48
48
49instance
49instance
50− GHasField field sub t
50+ GHasField field sub sub t t
51 => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
51 => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
52
52
53 gupcast r = M1 (K1 (r ^. gfield @field))
53 gupcast r = M1 (K1 (r ^. gfield @field))
@@ -84,7 +84,7 @@ class GSmashLeaf sub sup (w :: Bool) where
84 gsmashLeaf :: sup p -> sub p -> sub p
84 gsmashLeaf :: sup p -> sub p -> sub p
85
85
86instance
86instance
87− GHasField field sup t
87+ GHasField field sup sup t t
88 => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
88 => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
89 gsmashLeaf sup _ = M1 (K1 (sup ^. gfield @field))
89 gsmashLeaf sup _ = M1 (K1 (sup ^. gfield @field))
90
90
src/Data/Generics/Product/Positions.hs+49−42
@@ -1,5 +1,7 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE KindSignatures #-}
7{-# LANGUAGE KindSignatures #-}
@@ -27,73 +29,78 @@
27module Data.Generics.Product.Positions
29module Data.Generics.Product.Positions
28 ( -- *Lenses
30 ( -- *Lenses
29
31
30− -- $example
32+ -- $setup
31 HasPosition (..)
33 HasPosition (..)
34+ , HasPosition'
35+
36+ , getPosition
37+ , setPosition
32 ) where
38 ) where
33
39
34import Data.Generics.Internal.Lens
40import Data.Generics.Internal.Lens
41+import Data.Generics.Internal.Void
42+import Data.Generics.Internal.Families
35import Data.Generics.Product.Internal.Positions
43import Data.Generics.Product.Internal.Positions
36
44
37import Data.Kind (Constraint, Type)
45import Data.Kind (Constraint, Type)
38−import Data.Type.Bool (type (&&))
46+import Data.Type.Bool (type (&&), If)
39import GHC.Generics
47import GHC.Generics
40import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
48import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
41
49
42−-- $example
50+-- $setup
43−-- @
51+-- >>> :set -XTypeApplications
44−-- module Example where
52+-- >>> :set -XDataKinds
45−--
53+-- >>> :set -XDeriveGeneric
46−-- import Data.Generics.Product
47−-- import GHC.Generics
54+-- >>> import GHC.Generics
48−--
55+-- >>> :m +Data.Generics.Internal.Lens
56+-- >>> :m +Data.Function
57+-- >>> :{
49−-- data Human = Human
58+-- data Human = Human
50−-- { name :: String
59+-- { name :: String
51−-- , age :: Int
60+-- , age :: Int
52−-- , address :: String
61+-- , address :: String
53−-- }
62+-- }
54−-- deriving (Generic, Show)
63+-- deriving (Generic, Show)
55−--
56−-- human :: Human
64+-- human :: Human
57−-- human = Human \"Tunyasz\" 50 \"London\"
65+-- human = Human "Tunyasz" 50 "London"
58−-- @
66+-- :}
59
67
60-- |Records that have a field at a given position.
68-- |Records that have a field at a given position.
61−class HasPosition (i :: Nat) a s | s i -> a where
69+class HasPosition (i :: Nat) s t a b | s i -> a, s i b -> t where
62 -- |A lens that focuses on a field at a given position. Compatible with the
70 -- |A lens that focuses on a field at a given position. Compatible with the
63 -- lens package's 'Control.Lens.Lens' type.
71 -- lens package's 'Control.Lens.Lens' type.
64 --
72 --
65 -- >>> human ^. position @1
73 -- >>> human ^. position @1
66 -- "Tunyasz"
74 -- "Tunyasz"
67− -- >>> human & position @2 .~ "Berlin"
75+ -- >>> human & position @3 .~ "Berlin"
68 -- Human {name = "Tunyasz", age = 50, address = "Berlin"}
76 -- Human {name = "Tunyasz", age = 50, address = "Berlin"}
69− position :: Lens' s a
77+ position :: Lens s t a b
70− position f s
71− = fmap (flip (setPosition @i) s) (f (getPosition @i s))
72− -- = fmap (setPosition s) (f (getPosition s))
73
78
74− -- |Get positional field
79+type HasPosition' i s a = HasPosition i s s a a
75− --
76− -- >>> getPosition @1 human
77− -- "Tunyasz"
78− getPosition :: s -> a
79− getPosition s = s ^. position @i
80
80
81− -- |Set positional field
81+getPosition :: forall i s a. HasPosition' i s a => s -> a
82− --
83− -- >>> setPosition @2 (setField @1 "Tamas" human) 30
84− -- Human "Tamas" 30 "London"
85− setPosition :: a -> s -> s
86− setPosition = set (position @i)
82+getPosition s = s ^. position @i
87
83
88− {-# MINIMAL position | setPosition, getPosition #-}
84+setPosition :: forall i s a. HasPosition' i s a => a -> s -> s
85+setPosition = set (position @i)
89
86
90−instance
87+instance -- see Note [Changing type parameters]
91 ( Generic s
88 ( Generic s
92 , ErrorUnless i s (0 <? i && i <=? Size (Rep s))
89 , ErrorUnless i s (0 <? i && i <=? Size (Rep s))
90+ , Generic t
91+ , s' ~ Proxied s
92+ , GHasPosition' i (Rep s) a
93+ , GHasPosition' i (Rep s') a'
93− , GHasPosition 1 i (Rep s) a
94+ , GHasPosition 1 i (Rep s) (Rep t) a b
95+ , '(t', b') ~ If (IsParam a') '(Change s' (IndexOf a') b, P (IndexOf a') b) '(s', b)
96+ , t ~ UnProxied t'
94− ) => HasPosition i a s where
97+ ) => HasPosition i s t a b where
98+
99+ position f s = ravel (repLens . gposition @1 @i) f s
95
100
96− position = ravel (repLens . gposition @1 @i)
101+-- See Note [Uncluttering type signatures]
102+instance {-# OVERLAPPING #-} HasPosition f Void Void Void Void where
103+ position = undefined
97
104
98type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint where
105type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint where
99 ErrorUnless i s 'False
106 ErrorUnless i s 'False
src/Data/Generics/Product/Subtype.hs+34−25
@@ -28,36 +28,38 @@
28module Data.Generics.Product.Subtype
28module Data.Generics.Product.Subtype
29 ( -- *Lenses
29 ( -- *Lenses
30 --
30 --
31− -- $example
31+ -- $setup
32 Subtype (..)
32 Subtype (..)
33 ) where
33 ) where
34
34
35import Data.Generics.Internal.Lens
35import Data.Generics.Internal.Lens
36+import Data.Generics.Internal.Void
36import Data.Generics.Product.Internal.Subtype
37import Data.Generics.Product.Internal.Subtype
37
38
38import GHC.Generics (Generic (Rep, to, from) )
39import GHC.Generics (Generic (Rep, to, from) )
39
40
40−-- $example
41+-- $setup
41−-- @
42+-- >>> :set -XTypeApplications
42−-- module Example where
43+-- >>> :set -XDataKinds
43−--
44+-- >>> :set -XDeriveGeneric
44−-- import Data.Generics.Product
45+-- >>> :set -XDuplicateRecordFields
45−-- import GHC.Generics
46+-- >>> import GHC.Generics
46−--
47+-- >>> :m +Data.Generics.Internal.Lens
48+-- >>> :{
47−-- data Human = Human
49+-- data Human = Human
48−-- { name :: String
50+-- { name :: String
49−-- , age :: Int
51+-- , age :: Int
50−-- , address :: String
52+-- , address :: String
53+-- }
51−-- } deriving (Generic, Show)
54+-- deriving (Generic, Show)
52−--
53−-- data Animal = Animal
55+-- data Animal = Animal
54−-- { name :: String
56+-- { name :: String
55−-- , age :: Int
57+-- , age :: Int
58+-- }
56−-- } deriving (Generic, Show)
59+-- deriving (Generic, Show)
57−--
58−-- human :: Human
60+-- human :: Human
59−-- human = Human \"Tunyasz\" 50 \"London\"
61+-- human = Human "Tunyasz" 50 "London"
60−-- @
62+-- :}
61
63
62-- |Structural subtype relationship
64-- |Structural subtype relationship
63--
65--
@@ -94,11 +96,18 @@ class Subtype sup sub where
94
96
95 {-# MINIMAL super | smash, upcast #-}
97 {-# MINIMAL super | smash, upcast #-}
96
98
99+-- TODO: improve type error by showing a diff of fields
97instance
100instance
98− ( GSmash (Rep a) (Rep b)
99− , GUpcast (Rep a) (Rep b)
100− , Generic a
101+ ( Generic a
101 , Generic b
102 , Generic b
103+ , GSmash (Rep a) (Rep b)
104+ , GUpcast (Rep a) (Rep b)
102 ) => Subtype b a where
105 ) => Subtype b a where
103 smash p b = to $ gsmash (from p) (from b)
106 smash p b = to $ gsmash (from p) (from b)
104 upcast = to . gupcast . from
107 upcast = to . gupcast . from
108+
109+-- See Note [Uncluttering type signatures]
110+instance {-# OVERLAPPING #-} Subtype a Void where
111+ super = undefined
112+instance {-# OVERLAPPING #-} Subtype Void a where
113+ super = undefined
src/Data/Generics/Product/Typed.hs+22−18
@@ -26,35 +26,35 @@
26module Data.Generics.Product.Typed
26module Data.Generics.Product.Typed
27 ( -- *Lenses
27 ( -- *Lenses
28 --
28 --
29− -- $example
29+ -- $setup
30 HasType (..)
30 HasType (..)
31 ) where
31 ) where
32
32
33import Data.Generics.Internal.Families
33import Data.Generics.Internal.Families
34import Data.Generics.Internal.Lens
34import Data.Generics.Internal.Lens
35+import Data.Generics.Internal.Void
35import Data.Generics.Product.Internal.Typed
36import Data.Generics.Product.Internal.Typed
36
37
37import Data.Kind (Constraint, Type)
38import Data.Kind (Constraint, Type)
38import GHC.Generics (Generic (Rep))
39import GHC.Generics (Generic (Rep))
39import GHC.TypeLits (TypeError, ErrorMessage (..))
40import GHC.TypeLits (TypeError, ErrorMessage (..))
40
41
41−-- $example
42+-- $setup
42−-- @
43+-- >>> :set -XTypeApplications
43−-- module Example where
44+-- >>> :set -XDataKinds
44−--
45+-- >>> :set -XDeriveGeneric
45−-- import Data.Generics.Product
46−-- import GHC.Generics
46+-- >>> import GHC.Generics
47−--
47+-- >>> :m +Data.Generics.Internal.Lens
48+-- >>> :{
48−-- data Human = Human
49+-- data Human = Human
49−-- { name :: String
50+-- { name :: String
50−-- , age :: Int
51+-- , age :: Int
51−-- , address :: String
52+-- , address :: String
52−-- }
53+-- }
53−-- deriving (Generic, Show)
54+-- deriving (Generic, Show)
54−--
55−-- human :: Human
55+-- human :: Human
56−-- human = Human \"Tunyasz\" 50 \"London\"
56+-- human = Human "Tunyasz" 50 "London"
57−-- @
57+-- :}
58
58
59-- |Records that have a field with a unique type.
59-- |Records that have a field with a unique type.
60class HasType a s where
60class HasType a s where
@@ -85,6 +85,10 @@ instance
85
85
86 typed = ravel (repLens . gtyped)
86 typed = ravel (repLens . gtyped)
87
87
88+-- See Note [Uncluttering type signatures]
89+instance {-# OVERLAPPING #-} HasType a Void where
90+ typed = undefined
91+
88type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constraint where
92type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constraint where
89 ErrorUnlessOne a s 'None
93 ErrorUnlessOne a s 'None
90 = TypeError
94 = TypeError
src/Data/Generics/Sum/Any.hs+28−30
@@ -24,7 +24,7 @@
24module Data.Generics.Sum.Any
24module Data.Generics.Sum.Any
25 ( -- *Prisms
25 ( -- *Prisms
26 --
26 --
27− -- $example
27+ -- $setup
28 AsAny (..)
28 AsAny (..)
29 ) where
29 ) where
30
30
@@ -32,34 +32,31 @@ import Data.Generics.Internal.Lens
32import Data.Generics.Sum.Constructors
32import Data.Generics.Sum.Constructors
33import Data.Generics.Sum.Typed
33import Data.Generics.Sum.Typed
34
34
35−-- $example
35+-- $setup
36−-- @
36+-- >>> :set -XTypeApplications
37−-- module Example where
37+-- >>> :set -XDataKinds
38−--
38+-- >>> :set -XDeriveGeneric
39+-- >>> import GHC.Generics
39−-- import Data.Generics.Sum
40+-- >>> :m +Data.Generics.Internal.Lens
40−-- import GHC.Generics
41+-- >>> :{
41−--
42−-- data Animal
42+-- data Animal
43−-- = Dog Dog
43+-- = Dog Dog
44−-- | Cat Name Age
44+-- | Cat Name Age
45−-- | Duck Age
45+-- | Duck Age
46−-- deriving (Generic, Show)
46+-- deriving (Generic, Show)
47−--
48−-- data Dog = MkDog
47+-- data Dog
48+-- = MkDog
49−-- { name :: Name
49+-- { name :: Name
50−-- , age :: Age
50+-- , age :: Age
51−-- }
51+-- }
52−-- deriving (Generic, Show)
52+-- deriving (Generic, Show)
53−--
54−-- type Name = String
53+-- type Name = String
55−-- type Age = Int
54+-- type Age = Int
56−--
57−-- dog, cat, duck :: Animal
55+-- dog, cat, duck :: Animal
58−--
59−-- dog = Dog (MkDog "Shep" 3)
56+-- dog = Dog (MkDog "Shep" 3)
60−-- cat = Cat "Mog" 5
57+-- cat = Cat "Mog" 5
61−-- duck = Duck 2
58+-- duck = Duck 2
62−-- @
59+-- :}
63
60
64-- |Sums that have generic prisms.
61-- |Sums that have generic prisms.
65class AsAny (sel :: k) a s | s sel k -> a where
62class AsAny (sel :: k) a s | s sel k -> a where
@@ -78,7 +75,7 @@ class AsAny (sel :: k) a s | s sel k -> a where
78 -- >>> cat ^? _As @"Cat"
75 -- >>> cat ^? _As @"Cat"
79 -- Just ("Mog",5)
76 -- Just ("Mog",5)
80 -- >>> _As @"Cat" # ("Garfield", 6) :: Animal
77 -- >>> _As @"Cat" # ("Garfield", 6) :: Animal
81− -- Cat ("Garfield",6)
78+ -- Cat "Garfield" 6
82 -- >>> duck ^? _As @Age
79 -- >>> duck ^? _As @Age
83 -- Just 2
80 -- Just 2
84 _As :: Prism' s a
81 _As :: Prism' s a
@@ -88,3 +85,4 @@ instance AsConstructor ctor a s => AsAny ctor a s where
88
85
89instance AsType a s => AsAny a a s where
86instance AsType a s => AsAny a a s where
90 _As = _Typed @a
87 _As = _Typed @a
88+
src/Data/Generics/Sum/Constructors.hs+33−31
@@ -26,46 +26,44 @@
26module Data.Generics.Sum.Constructors
26module Data.Generics.Sum.Constructors
27 ( -- *Prisms
27 ( -- *Prisms
28
28
29− -- $example
29+ -- $setup
30 AsConstructor (..)
30 AsConstructor (..)
31 ) where
31 ) where
32
32
33import Data.Generics.Internal.Families
33import Data.Generics.Internal.Families
34import Data.Generics.Internal.Lens
34import Data.Generics.Internal.Lens
35+import Data.Generics.Internal.Void
35import Data.Generics.Sum.Internal.Constructors
36import Data.Generics.Sum.Internal.Constructors
36
37
37import Data.Kind (Constraint, Type)
38import Data.Kind (Constraint, Type)
38import GHC.Generics (Generic (Rep))
39import GHC.Generics (Generic (Rep))
39import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
40import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
40
41
41−-- $example
42+-- $setup
42−-- @
43+-- >>> :set -XTypeApplications
43−-- module Example where
44+-- >>> :set -XDataKinds
44−--
45+-- >>> :set -XDeriveGeneric
46+-- >>> import GHC.Generics
45−-- import Data.Generics.Sum
47+-- >>> :m +Data.Generics.Internal.Lens
46−-- import GHC.Generics
48+-- >>> :{
47−--
48−-- data Animal
49+-- data Animal
49−-- = Dog Dog
50+-- = Dog Dog
50−-- | Cat Name Age
51+-- | Cat Name Age
51−-- | Duck Age
52+-- | Duck Age
52−-- deriving (Generic, Show)
53+-- deriving (Generic, Show)
53−--
54−-- data Dog = MkDog
54+-- data Dog
55+-- = MkDog
55−-- { name :: Name
56+-- { name :: Name
56−-- , age :: Age
57+-- , age :: Age
57−-- }
58+-- }
58−-- deriving (Generic, Show)
59+-- deriving (Generic, Show)
59−--
60−-- type Name = String
60+-- type Name = String
61−-- type Age = Int
61+-- type Age = Int
62−--
63−-- dog, cat, duck :: Animal
62+-- dog, cat, duck :: Animal
64−--
65−-- dog = Dog (MkDog "Shep" 3)
63+-- dog = Dog (MkDog "Shep" 3)
66−-- cat = Cat "Mog" 5
64+-- cat = Cat "Mog" 5
67−-- duck = Duck 2
65+-- duck = Duck 2
68−-- @
66+-- :}
69
67
70-- |Sums that have a constructor with a given name.
68-- |Sums that have a constructor with a given name.
71class AsConstructor (ctor :: Symbol) a s | s ctor -> a where
69class AsConstructor (ctor :: Symbol) a s | s ctor -> a where
@@ -79,10 +77,10 @@ class AsConstructor (ctor :: Symbol) a s | s ctor -> a where
79 -- Nothing
77 -- Nothing
80 --
78 --
81 -- >>> cat ^? _Ctor @"Cat"
79 -- >>> cat ^? _Ctor @"Cat"
82− -- Just ("Mog", 5)
80+ -- Just ("Mog",5)
83 --
81 --
84 -- >>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
82 -- >>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
85− -- Cat ("Garfield", 6)
83+ -- Cat "Garfield" 6
86 _Ctor :: Prism' s a
84 _Ctor :: Prism' s a
87
85
88instance
86instance
@@ -93,6 +91,10 @@ instance
93
91
94 _Ctor = repIso . _GCtor @ctor
92 _Ctor = repIso . _GCtor @ctor
95
93
94+-- See Note [Uncluttering type signatures]
95+instance {-# OVERLAPPING #-} AsConstructor ctor Void Void where
96+ _Ctor = undefined
97+
96type family ErrorUnless (ctor :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
98type family ErrorUnless (ctor :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
97 ErrorUnless ctor s 'False
99 ErrorUnless ctor s 'False
98 = TypeError
100 = TypeError
src/Data/Generics/Sum/Subtype.hs+41−39
@@ -22,53 +22,47 @@
22module Data.Generics.Sum.Subtype
22module Data.Generics.Sum.Subtype
23 ( -- *Prisms
23 ( -- *Prisms
24 --
24 --
25− -- $example
25+ -- $setup
26 AsSubtype (..)
26 AsSubtype (..)
27 ) where
27 ) where
28
28
29import Data.Generics.Internal.Lens
29import Data.Generics.Internal.Lens
30+import Data.Generics.Internal.Void
30import Data.Generics.Sum.Internal.Subtype
31import Data.Generics.Sum.Internal.Subtype
31
32
32import GHC.Generics (Generic (Rep, to, from))
33import GHC.Generics (Generic (Rep, to, from))
33
34
34−-- $example
35+-- $setup
35−-- @
36+-- >>> :set -XTypeApplications
36−-- module Example where
37+-- >>> :set -XDataKinds
37−--
38+-- >>> :set -XDeriveGeneric
39+-- >>> import GHC.Generics
38−-- import Data.Generics.Sum
40+-- >>> :m +Data.Generics.Internal.Lens
39−-- import GHC.Generics
40−--
41+-- >>> :{
41−-- data Animal
42+-- data Animal
42−-- = Dog Dog
43+-- = Dog Dog
43−-- | Cat Name Age
44+-- | Cat Name Age
44−-- | Duck Age
45+-- | Duck Age
45−-- deriving (Generic, Show)
46+-- deriving (Generic, Show)
46−--
47−-- data FourLeggedAnimal
47+-- data FourLeggedAnimal
48−-- = Dog4 Dog
48+-- = Dog4 Dog
49−-- | Cat4 Name Age
49+-- | Cat4 Name Age
50−-- deriving (Generic, Show)
50+-- deriving (Generic, Show)
51−--
52−-- data Dog = MkDog
51+-- data Dog = MkDog
53−-- { name :: Name
52+-- { name :: Name
54−-- , age :: Age
53+-- , age :: Age
55−-- }
54+-- }
56−-- deriving (Generic, Show)
55+-- deriving (Generic, Show)
57−--
58−-- type Name = String
56+-- type Name = String
59−-- type Age = Int
57+-- type Age = Int
60−--
61−-- dog, cat, duck :: Animal
58+-- dog, cat, duck :: Animal
62−--
63−-- dog = Dog (MkDog "Shep" 3)
59+-- dog = Dog (MkDog "Shep" 3)
64−-- cat = Cat "Mog" 5
60+-- cat = Cat "Mog" 5
65−-- duck = Duck 2
61+-- duck = Duck 2
66−--
67−-- dog4, cat4 :: FourLeggedAnimal
62+-- dog4, cat4 :: FourLeggedAnimal
68−--
69−-- dog4 = Dog4 (MkDog "Snowy" 4)
63+-- dog4 = Dog4 (MkDog "Snowy" 4)
70−-- cat4 = Cat4 "Garfield" 6
64+-- cat4 = Cat4 "Garfield" 6
71−-- @
65+-- :}
72
66
73-- |Structural subtyping between sums. A sum 'Sub' is a subtype of another sum
67-- |Structural subtyping between sums. A sum 'Sub' is a subtype of another sum
74-- 'Sup' if a value of 'Sub' can be given (modulo naming of constructors)
68-- 'Sup' if a value of 'Sub' can be given (modulo naming of constructors)
@@ -106,3 +100,11 @@ instance
106
100
107 injectSub = to . ginjectSub . from
101 injectSub = to . ginjectSub . from
108 projectSub = either (Left . to) (Right . to) . gprojectSub . from
102 projectSub = either (Left . to) (Right . to) . gprojectSub . from
103+
104+-- See Note [Uncluttering type signatures]
105+instance {-# OVERLAPPING #-} AsSubtype a Void where
106+ injectSub = undefined
107+ projectSub = undefined
108+instance {-# OVERLAPPING #-} AsSubtype Void a where
109+ injectSub = undefined
110+ projectSub = undefined
src/Data/Generics/Sum/Typed.hs+38−29
@@ -25,7 +25,7 @@
25module Data.Generics.Sum.Typed
25module Data.Generics.Sum.Typed
26 ( -- *Prisms
26 ( -- *Prisms
27 --
27 --
28− -- $example
28+ -- $setup
29 AsType (..)
29 AsType (..)
30 ) where
30 ) where
31
31
@@ -36,35 +36,34 @@ import Data.Generics.Sum.Internal.Typed
36
36
37import Data.Generics.Internal.Families
37import Data.Generics.Internal.Families
38import Data.Generics.Internal.Lens
38import Data.Generics.Internal.Lens
39+import Data.Generics.Internal.Void
40+
41+-- $setup
42+-- >>> :set -XTypeApplications
43+-- >>> :set -XDataKinds
44+-- >>> :set -XDeriveGeneric
45+-- >>> import GHC.Generics
46+-- >>> :m +Data.Generics.Internal.Lens
47+-- >>> :{
48+-- data Animal
49+-- = Dog Dog
50+-- | Cat Name Age
51+-- | Duck Age
52+-- deriving (Generic, Show)
53+-- data Dog
54+-- = MkDog
55+-- { name :: Name
56+-- , age :: Age
57+-- }
58+-- deriving (Generic, Show)
59+-- type Name = String
60+-- type Age = Int
61+-- dog, cat, duck :: Animal
62+-- dog = Dog (MkDog "Shep" 3)
63+-- cat = Cat "Mog" 5
64+-- duck = Duck 2
65+-- :}
39
66
40−-- $example
41−-- @
42−-- module Example where
43−--
44−-- import Data.Generics.Sum
45−-- import GHC.Generics
46−--
47−-- data Animal
48−-- = Dog Dog
49−-- | Cat Name Age
50−-- | Duck Age
51−-- deriving (Generic, Show)
52−--
53−-- data Dog = MkDog
54−-- { name :: Name
55−-- , age :: Age
56−-- }
57−-- deriving (Generic, Show)
58−--
59−-- type Name = String
60−-- type Age = Int
61−--
62−-- dog, cat, duck :: Animal
63−--
64−-- dog = Dog (MkDog "Shep" 3)
65−-- cat = Cat "Mog" 5
66−-- duck = Duck 2
67−-- @
68
67
69-- |Sums that have a constructor with a field of the given type.
68-- |Sums that have a constructor with a field of the given type.
70class AsType a s where
69class AsType a s where
@@ -100,6 +99,16 @@ instance
100 projectTyped
99 projectTyped
101 = either (const Nothing) Just . gprojectTyped . from
100 = either (const Nothing) Just . gprojectTyped . from
102
101
102+-- See Note [Uncluttering type signatures]
103+instance {-# OVERLAPPING #-} AsType a Void where
104+ _Typed = undefined
105+ injectTyped = undefined
106+ projectTyped = undefined
107+instance {-# OVERLAPPING #-} AsType Void a where
108+ _Typed = undefined
109+ injectTyped = undefined
110+ projectTyped = undefined
111+
103type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constraint where
112type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constraint where
104 ErrorUnlessOne a s 'None
113 ErrorUnlessOne a s 'None
105 = TypeError
114 = TypeError
stack.yaml+1−1
@@ -1,4 +1,4 @@
1−resolver: lts-9.12
1+resolver: nightly-2017-11-25
2
2
3packages:
3packages:
4- '.'
4- '.'
test/Spec.hs+18−1
@@ -28,11 +28,20 @@ data Record2 = MkRecord2
28 { fieldA :: Int
28 { fieldA :: Int
29 } deriving Generic
29 } deriving Generic
30
30
31+data Record3 a = MkRecord3
32+ { fieldA :: a
33+ , fieldB :: Bool
34+ } deriving Generic
35+
36+type Lens' s a = Lens s s a a
31−type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s
37+type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
32
38
33fieldALensManual :: Lens' Record Int
39fieldALensManual :: Lens' Record Int
34fieldALensManual f (MkRecord a b) = (\a' -> MkRecord a' b) <$> f a
40fieldALensManual f (MkRecord a b) = (\a' -> MkRecord a' b) <$> f a
35
41
42+typeChangingManual :: Lens (Record3 a) (Record3 b) a b
43+typeChangingManual f (MkRecord3 a b) = (\a' -> MkRecord3 a' b) <$> f a
44+
36newtype L s a = L (Lens' s a)
45newtype L s a = L (Lens' s a)
37
46
38subtypeLensManual :: Lens' Record Record2
47subtypeLensManual :: Lens' Record Record2
@@ -60,7 +69,15 @@ fieldALensPos = position @1
60subtypeLensGeneric :: Lens' Record Record2
69subtypeLensGeneric :: Lens' Record Record2
61subtypeLensGeneric = super
70subtypeLensGeneric = super
62
71
72+typeChangingGeneric :: Lens (Record3 a) (Record3 b) a b
73+typeChangingGeneric = field @"fieldA"
74+
75+typeChangingGenericPos :: Lens (Record3 a) (Record3 b) a b
76+typeChangingGenericPos = position @1
77+
63inspect $ 'fieldALensManual === 'fieldALensName
78inspect $ 'fieldALensManual === 'fieldALensName
64inspect $ 'fieldALensManual === 'fieldALensType
79inspect $ 'fieldALensManual === 'fieldALensType
65inspect $ 'fieldALensManual === 'fieldALensPos
80inspect $ 'fieldALensManual === 'fieldALensPos
66inspect $ 'subtypeLensManual === 'subtypeLensGeneric
81inspect $ 'subtypeLensManual === 'subtypeLensGeneric
82+inspect $ 'typeChangingManual === 'typeChangingGeneric
83+--inspect $ 'typeChangingManual === 'typeChangingGenericPos
test/doctest.hsnew file+7
@@ -0,0 +1,7 @@
1+import Test.DocTest
2+main
3+ = doctest
4+ [ "-isrc"
5+ , "src/Data/Generics/Product.hs"
6+ , "src/Data/Generics/Sum.hs"
7+ ]