@@ -3,26 +3,46 @@
3[](https://travis-ci.org/kcsongor/generic-lens)
3[](https://travis-ci.org/kcsongor/generic-lens)
4[](https://hackage.haskell.org/package/generic-lens)
4[](https://hackage.haskell.org/package/generic-lens)
6−Generically derive lenses and prisms for data types.
6+Generically derive traversals, lenses and prisms.
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
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.
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
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.
22+Examples can be found in the `examples` and `tests` folders.
25+A typical module using `generic-lens` will usually have the following
28+{-# LANGUAGE DataKinds #-}
29+{-# LANGUAGE DeriveGeneric #-}
30+{-# LANGUAGE DuplicateRecordFields #-}
31+{-# LANGUAGE NoMonomorphismRestriction #-}
32+{-# LANGUAGE TypeApplications #-}
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
23−### Record fields
42+A lens identifies exactly one part of a product type, and allows querying and
25−Record fields can be accessed by their label:
45+### By name
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
34−```haskell
54+Record fields can be accessed by their label using the `field` lens.
35−>>> getField @"age" sally
38−>>> setField @"age" 26 sally
39−Person {name = "Sally", age = 26}
41>>> sally ^. field @"name"
57>>> sally ^. field @"name"
44>>> sally & field @"name" .~ "Tamas"
60>>> sally & field @"name" .~ "Tamas"
45Person {name = "Tamas", age = 25}
61Person {name = "Tamas", age = 25}
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.
68+Or for standalone use, the `getField` and `setField` functions can be used instead.
70+>>> getField @"age" sally
73+>>> setField @"age" 26 sally
74+Person {name = "Sally", age = 26}
77+When a non-existent field is requested, the library generates a helpful type error:
47>>> sally ^. field @"pet"
79>>> sally ^. field @"pet"
49− • The type Person does not contain a field named "pet"
81+ • The type Person does not contain a field named 'pet'
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)
88+>>> Second "woops" ^. field @"wurble"
90+>>> Second "woops" ^. field @"banana"
92+ • Not all constructors of the type Two
93+ contain a field named 'banana'.
94+ The offending constructors are:
99+The type of `field` is
101+field :: HasField name s t a b => Lens s t a b
103+Therefore it allows polymorphic (type-changing) updates, when the accessed field mentions type parameters.
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
62− deriving (Generic, Show)
108+ } deriving (Generic, Show)
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+
115+-- Foo {foo = ["10"]}
116+foo2 :: Foo [] String
117+foo2 = foo1 & field @"foo" %~ (maybeToList . fmap show)
119+This example shows that higher-kinded parameters can also be changed (`Maybe` -> `[]`).
69−### Positional fields
121+### By position
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):
@@ -79,12 +131,6 @@ polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
81```haskell
133```haskell
82−>>> getPosition @2 polygon
85−>>> setPosition @1 (Point 26 5 3) polygon
86−Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))
88>>> polygon ^. position @1 . position @2
134>>> polygon ^. position @1 . position @2
@@ -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
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:
101```haskell
147```haskell
102>>> (("hello", True), 5) ^. position @1 . position @2
148>>> (("hello", True), 5) ^. position @1 . position @2
150+>>> (("hello", True, "or"), 5, "even", "longer") ^. position @1 . position @2
106−### Typed fields
154+### By type
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:
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)
116−sally = Person "Sally" 25
123−>>> getTyped @String sally
126>>> setTyped @Int sally 26
163>>> setTyped @Int sally 26
127Person {name = "Sally", age = 26}
164Person {name = "Sally", age = 26}
129−>>> point ^. typed @Int
131− • The type Point contains multiple values of type Int; the choice of value is thus ambiguous
133−>>> point & typed @String .~ "Point"
135− • The type Point does not contain a value of type [Char]
138−### Structural subtyping
167+### By structure
169+The `super` lens generalises the `field` lens to focus on a collection rather
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.
143```haskell
174```haskell
144data Human = Human
175data Human = Human
@@ -157,16 +188,10 @@ human = Human {name = "Tunyasz", age = 50, address = "London"}
159```haskell
190```haskell
161−>>> upcast human :: Animal
191+>>> human ^. super @Animal
162Animal {name = "Tunyasz", age = 50}
192Animal {name = "Tunyasz", age = 50}
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"}
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}
@@ -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"}
211+Traversals allow multiple values to be queried or updated at the same time.
215+types :: HasTypes s a => Traversal s s a a
222+param :: HasParam pos s t a b => Traversal s t a b
228+constraints :: HasConstraints c s t => Applicative g => (forall a b . c a b => a -> g b) -> s -> g t
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).
188Constructor components can be accessed using the constructor's name:
239Constructor components can be accessed using the constructor's name:
@@ -205,19 +256,20 @@ Just (MkDog {name = "Shep", age = 4})
206>>> shep ^? _Ctor @"Cat"
257>>> shep ^? _Ctor @"Cat"
261+Constructors with multiple fields can be focused as a tuple
209>>> mog ^? _Ctor @"Cat"
264>>> mog ^? _Ctor @"Cat"
210Just ("Mog",5)
265Just ("Mog",5)
212>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
267>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
213Cat "Garfield" 6
268Cat "Garfield" 6
215−>>> donald ^? _Ctor @"Giraffe"
217− • The type Animal does not contain a constructor named "Giraffe"
220−### Typed constructors
272+### By type
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
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)
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
@@ -244,6 +296,9 @@ Just (MkDog {name = "Shep", age = 4})
244>>> donald ^? _Typed @Age
296>>> donald ^? _Typed @Age
299+>>> mog ^? _Typed @(Name, Age)
247>>> donald ^? _Typed @Float
302>>> donald ^? _Typed @Float
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