{-# LANGUAGE Rank2Types #-} {-# LANGUAGE Safe #-} {- | Copyright: (c) 2013-2016 Edward Kmett (c) 2019-2020 Kowainik SPDX-License-Identifier: MIT Maintainer: Kowainik Stability: Experimental Portability: Portable @since 0.5.0 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)](https://vrom911.github.io/blog/write-yourself-a-lens) == 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](https://hackage.haskell.org/package/lens) - [microlens](https://hackage.haskell.org/package/microlens) - [lens tutorial](http://hackage.haskell.org/package/lens-tutorial-1.0.3/docs/Control-Lens-Tutorial.html) -} module Relude.Extra.Lens ( Lens' , lens , view , set , over , (^.) , (.~) , (%~) ) where import Relude {- | 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 -} type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s {- | Creates 'Lens'' from the getter and setter. @since 0.5.0 -} lens :: (s -> a) -> (s -> a -> s) -> Lens' s a lens getter setter = \f s -> setter s <$> f (getter s) {-# INLINE lens #-} {- | Gets a value out of a structure using a getter. @since 0.5.0 -} view :: Lens' s a -> s -> a view l = getConst . l Const {-# INLINE view #-} {- | Sets the given value to the structure using a setter. @since 0.5.0 -} set :: Lens' s a -> a -> s -> s set l a = runIdentity . l (const (Identity a)) {-# INLINE set #-} {- | Applies the given function to the target. @since 0.5.0 -} over :: Lens' s a -> (a -> a) -> s -> s over l fa = runIdentity . l (Identity . fa) {-# INLINE over #-} {- | The operator form of 'view' with the arguments flipped. @since 0.5.0 -} infixl 8 ^. (^.) :: s -> Lens' s a -> a s ^. l = view l s {-# INLINE (^.) #-} {- | The operator form of 'set'. @since 0.5.0 -} infixr 4 .~ (.~) :: Lens' s a -> a -> s -> s (.~) = set {-# INLINE (.~) #-} {- | The operator form of 'over'. @since 0.5.0 -} infixr 4 %~ (%~) :: Lens' s a -> (a -> a) -> s -> s (%~) = over {-# INLINE (%~) #-}