packages feed

generic-lens-0.5.0.0: README.md

# generic-lens

[![Build Status](https://travis-ci.org/kcsongor/generic-lens.svg?branch=master)](https://travis-ci.org/kcsongor/generic-lens)
[![Hackage](https://img.shields.io/hackage/v/generic-lens.svg)](https://hackage.haskell.org/package/generic-lens)

Generically derive lenses and prisms for data types.

Available on [Hackage](https://hackage.haskell.org/package/generic-lens)

This package uses the GHC 8 `Generic` representation to derive various operations
on data structures with lens interfaces, including structural subtype
relationships between records and positional indexing into arbitrary product
types.

This is made possible by GHC 8's new Generics API, which provides metadata
at the type-level (previously only value-level metadata was available).

Examples can be found in the `examples` folder. This library makes heavy use of
[Visible Type Applications](https://ghc.haskell.org/trac/ghc/wiki/TypeApplication).

## Lenses

### Record fields

Record fields can be accessed by their label:

```haskell
data Person = Person { name :: String, age :: Int } deriving (Generic, Show)

sally :: Person
sally = Person "Sally" 25
```

```haskell
>>> getField @"age" sally
25

>>> setField @"age" 26 sally
Person {name = "Sally", age = 26}

>>> sally ^. field @"name"
"Sally"

>>> sally & field @"name" .~ "Tamas"
Person {name = "Tamas", age = 25}

>>> sally ^. field @"pet"
error:
  • The type Person does not contain a field named "pet"
```

If the accessed field is a type parameter that appears uniquely in the type,
then its type can be changed:

```haskell
data T a b c d = T
  { paramA :: a
  , paramB :: b
  , paramC :: c
  , paramD :: d
  }
  deriving (Generic, Show)

>>> t = T "a" (10 :: Int) 'c' False
>>> t & field @"paramA" .~ 'a' & field @"paramB" .~ False
T {paramA = 'a', paramB = False, paramC = 'c', paramD = False}
```

### Positional fields

Fields can be accessed by their position in the data structure (index starting at 1):

```haskell
data Point = Point Int Int Int deriving (Generic, Show)
data Polygon = Polygon Point Point Point deriving (Generic, Show)

polygon :: Polygon
polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
```

```haskell
>>> getPosition @2 polygon
Point 2 4 2

>>> setPosition @1 (Point 26 5 3) polygon
Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))

>>> polygon ^. position @1 . position @2
5

>>> polygon & position @3 . position @2 %~ (+10)
Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 17 (-2))

>>> polygon ^. position @10
error:
  • The type Polygon does not contain a field at position 10
```

Since tuples are an instance of `Generic`, they also have positional lenses:

```haskell
>>> (("hello", True), 5) ^. position @1 . position @2
True
```

### Typed fields

Fields can be accessed by their type in the data structure, assuming that this
type is unique:

```haskell
data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
data Point = Point Int Int Int deriving (Generic, Show)

sally :: Person
sally = Person "Sally" 25

point :: Point
point = Point 1 2 3
```

```haskell
>>> getTyped @String sally
"Sally"

>>> setTyped @Int sally 26
Person {name = "Sally", age = 26}

>>> point ^. typed @Int
error:
  • The type Point contains multiple values of type Int; the choice of value is thus ambiguous

>>> point & typed @String .~ "Point"
error:
  • The type Point does not contain a value of type [Char]
```

### Structural subtyping

A record is a (structural) `subtype' of another, if its fields are a superset of
those of the other.

```haskell
data Human = Human
  { name    :: String
  , age     :: Int
  , address :: String
  } deriving (Generic, Show)

data Animal = Animal
  { name    :: String
  , age     :: Int
  } deriving (Generic, Show)

human :: Human
human = Human {name = "Tunyasz", age = 50, address = "London"}
```

```haskell

>>> upcast human :: Animal
Animal {name = "Tunyasz", age = 50}

-- 'smash' plug the smaller structure into the larger one
>>> smash (Animal "dog" 10) human
Human {name = "dog", age = 10, address = "London"}

-- 'super' is a lens that focuses on a subrecord of a larger record:
>>> human ^. super @Animal
Animal {name = "Tunyasz", age = 50}
```

We can apply a function that operates on a supertype to the larger (subtype)
structure, by focusing on the supertype first:

```haskell
growUp :: Animal -> Animal
growUp (Animal name age) = Animal name (age + 50)

>>> human & super @Animal %~ growUp
Human {name = "Tunyasz", age = 60, address = "London"}
```

## Prisms

### Named constructors

Constructor components can be accessed using the constructor's name:

```haskell
type Name = String
type Age  = Int

data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
data Animal = Dog Dog | Cat Name Age | Duck Age deriving (Generic, Show)

shep = Dog (MkDog "Shep" 4)
mog = Cat "Mog" 5
donald = Duck 4
```

```haskell
>>> shep ^? _Ctor @"Dog"
Just (MkDog {name = "Shep", age = 4})

>>> shep ^? _Ctor @"Cat"
Nothing

>>> mog ^? _Ctor @"Cat"
Just ("Mog",5)

>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
Cat "Garfield" 6

>>> donald ^? _Ctor @"Giraffe"
error:
  • The type Animal does not contain a constructor named "Giraffe"
```

### Typed constructors

Constructor components can be accessed using the component's type, assuming
that this type is unique:

```haskell
type Name = String
type Age  = Int

data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
data Animal = Dog Dog | Cat (Name, Age) | Duck Age deriving (Generic, Show)

shep = Dog (MkDog "Shep" 4)
mog = Cat ("Mog", 5)
donald = Duck 4
```

```haskell
>>> mog ^? _Typed @Dog
Nothing

>>> shep ^? _Typed @Dog
Just (MkDog {name = "Shep", age = 4})

>>> donald ^? _Typed @Age
Just 4

>>> donald ^? _Typed @Float
error:
  • The type Animal does not contain a constructor whose field is of type Float

>>> _Typed @Age # 6 :: Animal
Duck 6
```

## Contributors

+ [Toby Shaw](https://github.com/TobyShaw)
+ [Will Jones](https://github.com/lunaris)