on bfcd00a help sign in
generic-lens/README.md
241 lines 5.5 kB history raw
1# generic-lens
3[![Build
4Status](https://travis-ci.org/kcsongor/generic-lens.svg?branch=master)](https://travis-ci.org/kcsongor/generic-lens)
6Generically derive lenses and prisms for data types.
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
10This package uses the GHC 8 `Generic` representation to derive various operations
11on data structures with lens interfaces, including structural subtype
12relationships between records and positional indexing into arbitrary product
13types.
15This is made possible by GHC 8's new Generics API, which provides metadata
16at the type-level (previously only value-level metadata was available).
18Examples can be found in the `examples` folder. This library makes heavy use of
19[Visible Type Applications](https://ghc.haskell.org/trac/ghc/wiki/TypeApplication).
21## Lenses
23### Record fields
25Record fields can be accessed by their label:
27```haskell
28data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
30sally :: Person
31sally = Person "Sally" 25
32```
34```haskell
35>>> getField @"age" sally
3625
38>>> setField @"age" 26 sally
39Person {name = "Sally", age = 26}
41>>> sally ^. field @"name"
42"Sally"
44>>> sally & field @"name" .~ "Tamas"
45Person {name = "Tamas", age = 25}
47>>> sally ^. field @"pet"
48error:
49 • The type Person does not contain a field named "pet"
50```
52### Positional fields
54Fields can be accessed by their position in the data structure (index starting at 1):
56```haskell
57data Point = Point Int Int Int deriving (Generic, Show)
58data Polygon = Polygon Point Point Point deriving (Generic, Show)
60polygon :: Polygon
61polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
62```
64```haskell
65>>> getPosition @2 polygon
66Point 2 4 2
68>>> setPosition @1 (Point 26 5 3) polygon
69Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))
71>>> polygon ^. position @1 . position @2
74>>> polygon & position @3 . position @2 %~ (+10)
75Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 17 (-2))
77>>> polygon ^. position @10
78error:
79 • The type Polygon does not contain a field at position 10
80```
82Since tuples are an instance of `Generic`, they also have positional lenses:
84```haskell
85>>> (("hello", True), 5) ^. position @1 . position @2
86True
87```
89### Typed fields
91Fields can be accessed by their type in the data structure, assuming that this
92type is unique:
94```haskell
95data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
96data Point = Point Int Int Int deriving (Generic, Show)
98sally :: Person
99sally = Person "Sally" 25
101point :: Point
102point = Point 1 2 3
103```
105```haskell
106>>> getTyped @String sally
107"Sally"
109>>> setTyped @Int sally 26
110Person {name = "Sally", age = 26}
112>>> point ^. typed @Int
113error:
114 • The type Point contains multiple values of type Int; the choice of value is thus ambiguous
116>>> point & typed @String .~ "Point"
117error:
118 • The type Point does not contain a value of type [Char]
119```
121### Structural subtyping
123A record is a (structural) `subtype' of another, if its fields are a superset of
124those of the other.
126```haskell
127data Human = Human
128 { name :: String
129 , age :: Int
130 , address :: String
131 } deriving (Generic, Show)
133data Animal = Animal
134 { name :: String
135 , age :: Int
136 } deriving (Generic, Show)
138human :: Human
139human = Human {name = "Tunyasz", age = 50, address = "London"}
140```
142```haskell
144>>> upcast human :: Animal
145Animal {name = "Tunyasz", age = 50}
147-- 'smash' plug the smaller structure into the larger one
148>>> smash (Animal "dog" 10) human
149Human {name = "dog", age = 10, address = "London"}
151-- 'super' is a lens that focuses on a subrecord of a larger record:
152>>> human ^. super @Animal
153Animal {name = "Tunyasz", age = 50}
154```
156We can apply a function that operates on a supertype to the larger (subtype)
157structure, by focusing on the supertype first:
159```haskell
160growUp :: Animal -> Animal
161growUp (Animal name age) = Animal name (age + 50)
163>>> human & super @Animal %~ growUp
164Human {name = "Tunyasz", age = 60, address = "London"}
165```
167## Prisms
169### Named constructors
171Constructor components can be accessed using the constructor's name:
173```haskell
174type Name = String
175type Age = Int
177data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
178data Animal = Dog Dog | Cat Name Age | Duck Age deriving (Generic, Show)
180shep = Dog (MkDog "Shep" 4)
181mog = Cat "Mog" 5
182donald = Duck 4
183```
185```haskell
186>>> shep ^? _Ctor @"Dog"
187Just (MkDog {name = "Shep", age = 4})
189>>> shep ^? _Ctor @"Cat"
190Nothing
192>>> mog ^? _Ctor @"Cat"
193Just ("Mog",5)
195>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
196Cat "Garfield" 6
198>>> donald ^? _Ctor @"Giraffe"
199error:
200 • The type Animal does not contain a constructor named "Giraffe"
201```
203### Typed constructors
205Constructor components can be accessed using the component's type, assuming
206that this type is unique:
208```haskell
209type Name = String
210type Age = Int
212data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
213data Animal = Dog Dog | Cat (Name, Age) | Duck Age deriving (Generic, Show)
215shep = Dog (MkDog "Shep" 4)
216mog = Cat ("Mog", 5)
217donald = Duck 4
218```
220```haskell
221>>> mog ^? _Typed @Dog
222Nothing
224>>> shep ^? _Typed @Dog
225Just (MkDog {name = "Shep", age = 4})
227>>> donald ^? _Typed @Age
228Just 4
230>>> donald ^? _Typed @Float
231error:
232 • The type Animal does not contain a constructor whose field is of type Float
234>>> _Typed @Age # 6 :: Animal
235Duck 6
236```
238## Contributors
240+ [Toby Shaw](https://github.com/TobyShaw)
241+ [Will Jones](https://github.com/lunaris)