on master help sign in
Update README
eeeaf4fCsongor Kiss
README.md+129−74
@@ -3,26 +3,46 @@
3[![Build 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)
4[![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)
5
5
6−Generically derive lenses and prisms for data types.
6+Generically derive traversals, lenses and prisms.
7
7
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
9
9
10+This library uses `GHC.Generics` to derive efficient optics (traversals, lenses
11+and prisms) for algebraic data types in a type-directed way, with a focus on
12+good type inference and error messages when possible.
13+
10This package uses the GHC 8 `Generic` representation to derive various operations
14This package uses the GHC 8 `Generic` representation to derive various operations
11on data structures with lens interfaces, including structural subtype
15on data structures with lens interfaces, including structural subtype
12relationships between records and positional indexing into arbitrary product
16relationships between records and positional indexing into arbitrary product
13types.
17types.
14
18
15−This is made possible by GHC 8's new Generics API, which provides metadata
19+The derived optics use the so-called van Laarhoven representation, thus are
16−at the type-level (previously only value-level metadata was available).
20+fully interoperable with the combinators found in mainstream lens libraries.
21+
22+Examples can be found in the `examples` and `tests` folders.
23+
24+## Preliminaries
25+A typical module using `generic-lens` will usually have the following
26+extensions turned on:
27+```haskell
28+{-# LANGUAGE DataKinds #-}
29+{-# LANGUAGE DeriveGeneric #-}
30+{-# LANGUAGE DuplicateRecordFields #-}
31+{-# LANGUAGE NoMonomorphismRestriction #-}
32+{-# LANGUAGE TypeApplications #-}
33+```
17
34
18−Examples can be found in the `examples` folder. This library makes heavy use of
35+# Taxonomy of optics
19−[Visible Type Applications](https://ghc.haskell.org/trac/ghc/wiki/TypeApplication).
36+Here is a comprehensive list of the optics exposed by `generic-lens`. The
37+combinators each allows a different way of identifying certain parts of
38+algebraic data types.
20
39
21## Lenses
40## Lenses
22
41
23−### Record fields
42+A lens identifies exactly one part of a product type, and allows querying and
43+updating it.
24
44
25−Record fields can be accessed by their label:
45+### By name
26
46
27```haskell
47```haskell
28data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
48data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
@@ -31,42 +51,74 @@ sally :: Person
31sally = Person "Sally" 25
51sally = Person "Sally" 25
32```
52```
33
53
34−```haskell
54+Record fields can be accessed by their label using the `field` lens.
35−>>> getField @"age" sally
36−25
37−
38−>>> setField @"age" 26 sally
39−Person {name = "Sally", age = 26}
40
55
56+```haskell
41>>> sally ^. field @"name"
57>>> sally ^. field @"name"
42"Sally"
58"Sally"
43
59
44>>> sally & field @"name" .~ "Tamas"
60>>> sally & field @"name" .~ "Tamas"
45Person {name = "Tamas", age = 25}
61Person {name = "Tamas", age = 25}
62+```
63+Here we use [visible type application](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#visible-type-application)
64+to specify which field we're interested in, and use the `^.` and `.~` combinators from a lens library
65+([lens](https://hackage.haskell.org/package/lens), [microlens](https://hackage.haskell.org/package/microlens), etc.)
66+to query and update the field.
67+
68+Or for standalone use, the `getField` and `setField` functions can be used instead.
69+```haskell
70+>>> getField @"age" sally
71+25
72+
73+>>> setField @"age" 26 sally
74+Person {name = "Sally", age = 26}
75+```
46
76
77+When a non-existent field is requested, the library generates a helpful type error:
78+```haskell
47>>> sally ^. field @"pet"
79>>> sally ^. field @"pet"
48error:
80error:
49− • The type Person does not contain a field named "pet"
81+ • The type Person does not contain a field named 'pet'
50```
82```
51
83
52−If the accessed field is a type parameter that appears uniquely in the type,
84+For types with multiple constructors, we can still use `field` as long as all constructors contain the required field
53−then its type can be changed:
85+```haskell
86+data Two = First { wurble :: String, banana :: Int } | Second { wurble :: String } deriving (Generic, Show)
87+
88+>>> Second "woops" ^. field @"wurble"
89+"woops"
90+>>> Second "woops" ^. field @"banana"
91+ ...
92+ • Not all constructors of the type Two
93+ contain a field named 'banana'.
94+ The offending constructors are:
95+ • Second
96+ ...
97+```
98+
99+The type of `field` is
100+```haskell
101+field :: HasField name s t a b => Lens s t a b
102+```
103+Therefore it allows polymorphic (type-changing) updates, when the accessed field mentions type parameters.
54
104
55```haskell
105```haskell
56−data T a b c d = T
106+data Foo f a = Foo
57− { paramA :: a
107+ { foo :: f a
58− , paramB :: b
59− , paramC :: c
60− , paramD :: d
61− }
62− deriving (Generic, Show)
108+ } deriving (Generic, Show)
63−
109+
64−>>> t = T "a" (10 :: Int) 'c' False
110+foo1 :: Foo Maybe Int
65−>>> t & field @"paramA" .~ 'a' & field @"paramB" .~ False
111+foo1 = Foo (Just 10)
66−T {paramA = 'a', paramB = False, paramC = 'c', paramD = False}
112+
113+-- |
114+-- >>> foo2
115+-- Foo {foo = ["10"]}
116+foo2 :: Foo [] String
117+foo2 = foo1 & field @"foo" %~ (maybeToList . fmap show)
67```
118```
119+This example shows that higher-kinded parameters can also be changed (`Maybe` -> `[]`).
68
120
69−### Positional fields
121+### By position
70
122
71Fields can be accessed by their position in the data structure (index starting at 1):
123Fields can be accessed by their position in the data structure (index starting at 1):
72
124
@@ -79,12 +131,6 @@ polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
79```
131```
80
132
81```haskell
133```haskell
82−>>> getPosition @2 polygon
83−Point 2 4 2
84−
85−>>> setPosition @1 (Point 26 5 3) polygon
86−Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))
87−
88>>> polygon ^. position @1 . position @2
134>>> polygon ^. position @1 . position @2
895
1355
90
136
@@ -96,49 +142,34 @@ error:
96 • The type Polygon does not contain a field at position 10
142 • The type Polygon does not contain a field at position 10
97```
143```
98
144
99−Since tuples are an instance of `Generic`, they also have positional lenses:
145+Since tuples are an instance of `Generic`, the positional lens readily works:
100
146
101```haskell
147```haskell
102>>> (("hello", True), 5) ^. position @1 . position @2
148>>> (("hello", True), 5) ^. position @1 . position @2
103True
149True
150+>>> (("hello", True, "or"), 5, "even", "longer") ^. position @1 . position @2
151+True
104```
152```
105
153
106−### Typed fields
154+### By type
107
155
108Fields can be accessed by their type in the data structure, assuming that this
156Fields can be accessed by their type in the data structure, assuming that this
109type is unique:
157type is unique:
110
158
111```haskell
159```haskell
112−data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
160+>>> sally ^. typed @String
113−data Point = Point Int Int Int deriving (Generic, Show)
114−
115−sally :: Person
116−sally = Person "Sally" 25
117−
118−point :: Point
119−point = Point 1 2 3
120−```
121−
122−```haskell
123−>>> getTyped @String sally
124"Sally"
161"Sally"
125
162
126>>> setTyped @Int sally 26
163>>> setTyped @Int sally 26
127Person {name = "Sally", age = 26}
164Person {name = "Sally", age = 26}
128−
129−>>> point ^. typed @Int
130−error:
131− • The type Point contains multiple values of type Int; the choice of value is thus ambiguous
132−
133−>>> point & typed @String .~ "Point"
134−error:
135− • The type Point does not contain a value of type [Char]
136```
165```
137
166
138−### Structural subtyping
167+### By structure
139
168
169+The `super` lens generalises the `field` lens to focus on a collection rather
170+than a single field.
140−A record is a (structural) `subtype' of another, if its fields are a superset of
171+We can say that a record is a (structural) `subtype' of another, if its fields
141−those of the other.
172+are a superset of those of the other.
142
173
143```haskell
174```haskell
144data Human = Human
175data Human = Human
@@ -157,16 +188,10 @@ human = Human {name = "Tunyasz", age = 50, address = "London"}
157```
188```
158
189
159```haskell
190```haskell
160−
161−>>> upcast human :: Animal
191+>>> human ^. super @Animal
162Animal {name = "Tunyasz", age = 50}
192Animal {name = "Tunyasz", age = 50}
163
193
164−-- 'smash' plug the smaller structure into the larger one
194+>>> upcast human :: Animal
165−>>> smash (Animal "dog" 10) human
166−Human {name = "dog", age = 10, address = "London"}
167−
168−-- 'super' is a lens that focuses on a subrecord of a larger record:
169−>>> human ^. super @Animal
170Animal {name = "Tunyasz", age = 50}
195Animal {name = "Tunyasz", age = 50}
171```
196```
172
197
@@ -181,9 +206,35 @@ growUp (Animal name age) = Animal name (age + 50)
181Human {name = "Tunyasz", age = 60, address = "London"}
206Human {name = "Tunyasz", age = 60, address = "London"}
182```
207```
183
208
209+## Traversals
210+
211+Traversals allow multiple values to be queried or updated at the same time.
212+
213+### By type
214+```haskell
215+types :: HasTypes s a => Traversal s s a a
216+```
217+TODO.
218+
219+### By parameter
220+TODO.
221+```haskell
222+param :: HasParam pos s t a b => Traversal s t a b
223+```
224+
225+### By constraint
226+TODO.
227+```haskell
228+constraints :: HasConstraints c s t => Applicative g => (forall a b . c a b => a -> g b) -> s -> g t
229+```
230+
184## Prisms
231## Prisms
185
232
186−### Named constructors
233+A prism focuses on one part of a sum type (which might not be present). Other
234+than querying the type, they can be "turned around" to inject the data into the
235+sum (like a constructor).
236+
237+### By name
187
238
188Constructor components can be accessed using the constructor's name:
239Constructor components can be accessed using the constructor's name:
189
240
@@ -205,19 +256,20 @@ Just (MkDog {name = "Shep", age = 4})
205
256
206>>> shep ^? _Ctor @"Cat"
257>>> shep ^? _Ctor @"Cat"
207Nothing
258Nothing
259+```
208
260
261+Constructors with multiple fields can be focused as a tuple
262+
263+```
209>>> mog ^? _Ctor @"Cat"
264>>> mog ^? _Ctor @"Cat"
210Just ("Mog",5)
265Just ("Mog",5)
211
266
212>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
267>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
213Cat "Garfield" 6
268Cat "Garfield" 6
214
269
215−>>> donald ^? _Ctor @"Giraffe"
216−error:
217− • The type Animal does not contain a constructor named "Giraffe"
218```
270```
219
271
220−### Typed constructors
272+### By type
221
273
222Constructor components can be accessed using the component's type, assuming
274Constructor components can be accessed using the component's type, assuming
223that this type is unique:
275that this type is unique:
@@ -227,10 +279,10 @@ type Name = String
227type Age = Int
279type Age = Int
228
280
229data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
281data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
230−data Animal = Dog Dog | Cat (Name, Age) | Duck Age deriving (Generic, Show)
282+data Animal = Dog Dog | Cat Name Age | Duck Age deriving (Generic, Show)
231
283
232shep = Dog (MkDog "Shep" 4)
284shep = Dog (MkDog "Shep" 4)
233−mog = Cat ("Mog", 5)
285+mog = Cat "Mog" 5
234donald = Duck 4
286donald = Duck 4
235```
287```
236
288
@@ -244,6 +296,9 @@ Just (MkDog {name = "Shep", age = 4})
244>>> donald ^? _Typed @Age
296>>> donald ^? _Typed @Age
245Just 4
297Just 4
246
298
299+>>> mog ^? _Typed @(Name, Age)
300+("Mog", 5)
301+
247>>> donald ^? _Typed @Float
302>>> donald ^? _Typed @Float
248error:
303error:
249 • The type Animal does not contain a constructor whose field is of type Float
304 • The type Animal does not contain a constructor whose field is of type Float
examples/Biscuits.hs+5−12
@@ -1,16 +1,14 @@
1−{-# LANGUAGE ConstraintKinds #-}
1+{-# LANGUAGE DuplicateRecordFields #-}
2+{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE DuplicateRecordFields #-}
4{-# LANGUAGE DuplicateRecordFields #-}
4−{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE NoMonomorphismRestriction #-}
5{-# LANGUAGE NoMonomorphismRestriction #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE TypeApplications #-}
6{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeInType #-}
10
7
11module Main where
8module Main where
12
9
13import Control.Lens
10import Control.Lens
11+import Data.Maybe (maybeToList)
14import Data.Generics.Product
12import Data.Generics.Product
15import Data.Generics.Sum
13import Data.Generics.Sum
16import GHC.Generics (Generic)
14import GHC.Generics (Generic)
@@ -126,14 +124,10 @@ data WeighedItem = WItem
126 } deriving (Generic, Show)
124 } deriving (Generic, Show)
127
125
128data D = DInt Int | DPair Bool String
126data D = DInt Int | DPair Bool String
129−
130−deriving instance Generic D
127+ deriving (Generic, Show)
131−deriving instance Show D
132
128
133data E = EInt Int | EPair Bool String | EChar Char
129data E = EInt Int | EPair Bool String | EChar Char
134−
135−deriving instance Generic E
130+ deriving (Generic, Show)
136−deriving instance Show E
137
131
138costInc :: HasTypes t Cost => t -> t
132costInc :: HasTypes t Cost => t -> t
139costInc = over (types @Cost) (\(Cost c) -> Cost (c + 5))
133costInc = over (types @Cost) (\(Cost c) -> Cost (c + 5))
@@ -146,4 +140,3 @@ treeIncParam = over (param @0) (+ 1)
146
140
147instance Functor Invoice where
141instance Functor Invoice where
148 fmap = over (param @0)
142 fmap = over (param @0)
149−
src/Data/Generics/Product/Fields.hs+1−1
@@ -31,7 +31,7 @@ module Data.Generics.Product.Fields
31
31
32 -- $setup
32 -- $setup
33 HasField (..)
33 HasField (..)
34− , HasField'
34+ , HasField' (..)
35
35
36 , getField
36 , getField
37 , setField
37 , setField
src/Data/Generics/Product/Positions.hs+1−1
@@ -32,7 +32,7 @@ module Data.Generics.Product.Positions
32
32
33 -- $setup
33 -- $setup
34 HasPosition (..)
34 HasPosition (..)
35− , HasPosition'
35+ , HasPosition' (..)
36
36
37 , getPosition
37 , getPosition
38 , setPosition
38 , setPosition