Relude.Extra.Lens

relude-1.2.2.0: Safe, performant, user-friendly and lightweight Haskell Standard Library

Copyright | (c) 2012-2016 Edward Kmett (c) 2019-2023 Kowainik
License | MIT
Maintainer | Kowainik <xrom.xkov@gmail.com>
Stability | Stable
Portability | Portable
Safe Haskell | Safe
Language | Haskell2010

Relude.Extra.Lens

Description

This module aims to provide a minimal implementation of `lens` package required for basic usage. All functions are compatible with the real `lens` package therefore if you need to expand to the full version the process should be straightforward.

Main ideas implemented in this module are described in the following blog post:

- Write yourself a lens (by Veronika Romashkina)

## Usage

To use lenses in your project, you don't need to add any other dependency rather than `relude`. You should add the import of this module in the place of lenses usage:

    import Relude.Extra.Lens

## Example

To understand better how to use this module lets look at some simple example. Let's say we have the user data type in our system:

    data User = User
        { userName    :: Text
        , userAge     :: Int
        , userAddress :: Address
        } deriving (Show)

    data Address = Address
        { addressCountry :: Text
        , addressCity    :: Text
        , addressIndex   :: Text
        } deriving (Show)

To create the lens for the `userName` field we can use `lens` function and manually writing getter and setter function:

    nameL :: Lens' User Text
    nameL = lens getter setter
      where
        getter :: User -> Text
        getter = userName

        setter :: User -> Text -> User
        setter user newName = user {userName = newName}

In this manner, we can create other lenses for our User data type.

    ageL     :: Lens' User Int
    addressL :: Lens' User Address
    countryL :: Lens' User Text
    cityL    :: Lens' User Text
    indexL   :: Lens' User Text

Note: here we are using composition of the lenses for `userAddress` field. If we have

    addressCityL :: Lens' Address Text

then

    cityL = addressL . addressCityL

Let's say we have some sample user

    user :: User
    user = User
        { userName = "John"
        , userAge  = 42
        , userAddress = Address
            { addressCountry = "UK"
            , addressCity    = "London"
            , addressIndex   = "XXX"
            }
        }

To view the fields of the User data type we can use `view` or `^.`

    >>> view ageL user
    42
    >>> user ^. cityL
    "London"

If we want to change any of the user's data, we should use `set` or `.~`

    >>> set nameL "Johnny" user
    >>> user & indexL .~ "YYY"

`over` or `%~` operator could be useful when, for example, you want to increase the age by one on the user's birthday:

    >>> over ageL succ user
    >>> user & ageL %~ succ

## Migration

This module is not supposed to be the replacement for the `lens` package. One of the reasons why one would want to migrate to `lens` or `microlens` is that the functional in `relude` is limited to just vital lens functions.

To migrate to `lens` or `microlens` package add the required library to the dependencies list in the `.cabal` file and replace the import from `relude` library

    import Relude.Extra.Lens

to the one of this correspondingly:

- `lens`:

      import Control.Lens

- `microlens`:

      import Lens.Micro

And that's all! No need to change the types or implementation of the functions you used `Relude.Extra.Lens` in.

## Links

- lens
- microlens
- lens tutorial

Synopsis

- type Lens' s a = forall (f :: Type -> Type). Functor f => (a -> f a) -> s -> f s
- lens :: (s -> a) -> (s -> a -> s) -> Lens' s a
- view :: Lens' s a -> s -> a
- set :: Lens' s a -> a -> s -> s
- over :: Lens' s a -> (a -> a) -> s -> s
- (^.) :: s -> Lens' s a -> a
- (.~) :: Lens' s a -> a -> s -> s
- (%~) :: Lens' s a -> (a -> a) -> s -> s

# Documentation

type Lens' s a = forall (f :: Type -> Type). Functor f => (a -> f a) -> s -> f s #

The monomorphic lenses which don't change the type of the container (or of the value inside). It has a `Functor` constraint, and since both `Const` and `Identity` are functors, it can be used whenever a getter or a setter is needed.

- `a` is the type of the value inside of structure
- `s` is the type of the whole structure

Since: 0.5.0

lens :: (s -> a) -> (s -> a -> s) -> Lens' s a #

Creates `Lens'` from the getter and setter.

Since: 0.5.0

view :: Lens' s a -> s -> a #

Gets a value out of a structure using a getter.

Since: 0.5.0

set :: Lens' s a -> a -> s -> s #

Sets the given value to the structure using a setter.

Since: 0.5.0

over :: Lens' s a -> (a -> a) -> s -> s #

Applies the given function to the target.

Since: 0.5.0

(^.) :: s -> Lens' s a -> a infixl 8 #

The operator form of `view` with the arguments flipped.

Since: 0.5.0

(.~) :: Lens' s a -> a -> s -> s infixr 4 #

The operator form of `set`.

Since: 0.5.0

(%~) :: Lens' s a -> (a -> a) -> s -> s infixr 4 #

The operator form of `over`.

Since: 0.5.0