{-# OPTIONS_GHC -Wno-redundant-constraints #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} {- | Module : Prolens Copyright : (c) 2020-2021 Kowainik SPDX-License-Identifier : MPL-2.0 Maintainer : Kowainik Stability : Stable Portability : Portable The @prolens@ package is a Haskell library with a minimal and lightweight implementation of optics based on 'Profunctor's. __'Optic'__ is a high-level concept for values that provide composable access to different parts of structures. "Prolens" implements the following optics: * 'Lens' — composable getters and setters * 'Prism' — composable constructors and deconstructors * 'Traversal' — composable data structures visitors == Usage To use lenses or prisms in your project, you need to add @prolens@ package as the dependency in the @build-depends@ field of your @.cabal@ file. E.g.: @ build-depends: prolens ^>= 0.0.0.0 @ You should add the import of this module in the place of lenses usage: @ __import__ "Prolens" @ == Creating your own optics We show in each section of this module how to create values of each kind of optics. ⚠️ __The general crucial rule__ for achieving maximum performance: always add @\{\-\# INLINE ... \#\-\}@ pragmas to your optics. == Typeclasses table The below table shows required constraints for each 'Optic': +-------------+------------------------------+ | Optic | Constraints | +=============+==============================+ | 'Lens' | @'Strong' p@ | +-------------+------------------------------+ | 'Prism' | @'Choice' p@ | +-------------+------------------------------+ | 'Traversal' | @('Choice' p, 'Monoidal' p)@ | +-------------+------------------------------+ == Usage table: get, set, modify Here is a go-to table on how to use getter, setters and modifiers with different 'Optic's. +-------------+------------------+--------------+------------------+------------------+-----------------+-----------------+ | | get | get operator | set | set operator | modify | modify operator | +=============+==================+==============+==================+==================+=================+=================+ | 'Lens' | @'view' l x@ | @x '^.' l@ | @'set' l new x@ | @x & l '.~' new@ | @'over' l f x@ | @x & l '%~' f@ | +-------------+------------------+--------------+------------------+------------------+-----------------+-----------------+ | 'Prism' | @'preview' _L x@ | - | @'set' _L new x@ | - | @'over' _L f x@ | - | +-------------+------------------+--------------+------------------+------------------+-----------------+-----------------+ | 'Traversal' | @'view' l x@ | - | @'set' l new x@ | - | @'over' l f x@ | - | +-------------+------------------+--------------+------------------+------------------+-----------------+-----------------+ @since 0.0.0.0 -} module Prolens ( -- * Profunctor typeclass Profunctor (..) -- * Optics , Optic -- * Lenses -- $lenses -- ** Lenses types , Lens , Lens' -- ** Strong typeclass , Strong (..) -- ** Lenses functions , set , over , view , lens -- ** Lenses operators , (^.) , (.~) , (%~) -- ** Standard lenses , fstL , sndL -- * Prisms -- $prisms -- ** Prism types , Prism , Prism' -- ** Choice typeclass , Choice (..) -- ** Prism functions , prism , prism' , preview -- ** Standard Prisms , _Just , _Left , _Right -- * Traversals -- ** Traversal types , Traversal -- ** Monoidal typeclass , Monoidal (..) -- ** Traversal functions , traverseOf -- ** Standard traversals , eachPair , eachMaybe , eachList -- * Internal data types , Forget (..) , Fun (..) ) where import Control.Applicative (Const (..), liftA2) import Data.Coerce (coerce) import Data.Monoid (First (..)) -- $setup -- >>> import Data.Function ((&)) {- | The type @p@ is called 'Profunctor' and it means, that a value of type @p in out@ takes a value of type @in@ as an argument (input) and outputs a value of type @out@. 'Profunctor' allows mapping of inputs and outputs. @ +----> Result input | | +--> Original profunctor | +-> Original input | + + + dimap :: (in2 -> in1) -> (out1 -> out2) -> p in1 out1 -> p in2 out2 + + | +-> Result output | +-> Original output @ Speaking in terms of other abstractions, 'Profunctor' is 'Data.Functor.Contravariant.Contravariant' in the first type argument (type variable @in@) and 'Functor' in the second type argument (type variable @out@). Moreover, @p in@ must have 'Functor' instance first to implement the 'Profunctor' instance. This required using @QuantifiedConstraints@. @ Contravariant <---+ | +-+-+ + + (forall a . Functor (p a)) => Profunctor p a b + + + | | | +--> Quantified constraint +++ | Functor <--+ @ Instances of 'Profunctor' should satisfy the following laws: * __Identity:__ @'dimap' 'id' 'id' ≡ 'id'@ * __Composition:__ @'dimap' (inAB . inBC) (outYZ . outXY) ≡ 'dimap' inBC outYZ . 'dimap' inAB outXY@ @since 0.0.0.0 -} -- type Profunctor :: (Type -> Type -> Type) -> Constraint class (forall a . Functor (p a)) => Profunctor p where dimap :: (in2 -> in1) -- ^ Map input -> (out1 -> out2) -- ^ Map output -> p in1 out1 -- ^ Take @in1@ as input and return @out1@ -> p in2 out2 -- ^ Take @in2@ as input and return @out2@ -- | @since 0.0.0.0 instance Profunctor (->) where dimap :: (in2 -> in1) -> (out1 -> out2) -> (in1 -> out1) -> (in2 -> out2) dimap in21 out12 f = out12 . f . in21 {-# INLINE dimap #-} {- | @'Fun' m a b@ is a wrapper for function @a -> m b@. @since 0.0.0.0 -} newtype Fun m a b = Fun { unFun :: a -> m b } -- | @since 0.0.0.0 instance Functor m => Functor (Fun m x) where fmap :: (a -> b) -> Fun m x a -> Fun m x b fmap f (Fun xma) = Fun (fmap f . xma) {-# INLINE fmap #-} -- | @since 0.0.0.0 instance Functor m => Profunctor (Fun m) where dimap :: (in2 -> in1) -> (out1 -> out2) -> Fun m in1 out1 -> Fun m in2 out2 dimap in21 out12 (Fun f) = Fun (fmap out12 . f . in21) {-# INLINE dimap #-} {- | 'Strong' is a 'Profunctor' that can be lifted to take a pair as an input and return a pair. The second element of a pair (variable of type @c@) can be of any type, and you can decide what type it should be. This is convenient for implementing various functions. E.g. 'lens' uses this fact. Instances of 'Strong' should satisfy the following laws: * __'first' via 'second' swap:__ @'first' ≡ 'dimap' 'Data.Tuple.swap' 'Data.Tuple.swap' . 'second'@ * __'second' via 'first' swap:__ @'second' ≡ 'dimap' 'Data.Tuple.swap' 'Data.Tuple.swap' . 'first'@ * __Fst functor:__ @'dimap' 'fst' 'id' ≡ 'fmap' 'fst' . 'first'@ * __Snd functor:__ @'dimap' 'snd' 'id' ≡ 'fmap' 'snd' . 'second'@ * __Distribution over 'first':__ @'dimap' ('second' f) 'id' . 'first' ≡ 'fmap' ('second' f) . 'first'@ * __Distribution over 'second':__ @'dimap' ('first' f) 'id' . 'second' ≡ 'fmap' ('first' f) . 'second'@ * __Associativity of 'first':__ @'first' . 'first' ≡ 'dimap' (\\((a, b), c) -> (a, (b, c))) (\\(a, (b, c)) -> ((a, b), c)) . 'first'@ * __Associativity of 'second':__ @'second' . 'second' ≡ 'dimap' (\\(a, (b, c)) -> ((a, b), c)) (\\((a, b), c) -> (a, (b, c))) . 'second'@ @since 0.0.0.0 -} class Profunctor p => Strong p where first :: p a b -> p (a, c) (b, c) second :: p a b -> p (c, a) (c, b) -- | @since 0.0.0.0 instance Strong (->) where first :: (a -> b) -> (a, c) -> (b, c) first ab (a, c) = (ab a, c) {-# INLINE first #-} second :: (a -> b) -> (c, a) -> (c, b) second ab (c, a) = (c, ab a) {-# INLINE second #-} -- | @since 0.0.0.0 instance (Functor m) => Strong (Fun m) where first :: Fun m a b -> Fun m (a, c) (b, c) first (Fun amb) = Fun (\(a, c) -> fmap (, c) (amb a)) {-# INLINE first #-} second :: Fun m a b -> Fun m (c, a) (c, b) second (Fun amb) = Fun (\(c, a) -> fmap (c,) (amb a)) {-# INLINE second #-} {- | 'Choice' is a 'Profunctor' that can be lifted to work with 'Either' given input or some other value. The other element of 'Either' (variable of type @c@) can be of any type, and you can decide what type it should be. This is convenient for implementing various functions. E.g. 'prism' uses this fact. Assuming, we have the following functions in scope: @ swapEither :: Either a b -> Either b a unnestLeft :: Either (Either a b) c -> Either a (Either b c) unnestRight :: Either a (Either b c) -> Either (Either a b) c @ Instances of 'Choice' should satisfy the following laws: * __'left' via 'right' swap:__ @'left' ≡ 'dimap' swapEither swapEither . 'right'@ * __'right' via 'left' swap:__ @'right' ≡ 'dimap' swapEither swapEither . 'left'@ * __'Left' functor:__ @'fmap' 'Left' ≡ 'dimap' 'Left' 'id' . 'left'@ * __'Right' functor:__ @'fmap' 'Right' ≡ 'dimap' 'Right' 'id' . 'right'@ * __Distribution over 'left':__ @'dimap' ('right' f) 'id' . 'left' ≡ 'fmap' ('right' f) . 'left'@ * __Distribution over 'right':__ @'dimap' ('left' f) 'id' . 'right' ≡ 'fmap' ('left' f) . 'right'@ * __Associativity of 'left':__ @'left' . 'left' ≡ 'dimap' unnestLeft unnestRight . 'left'@ * __Associativity of 'right':__ @'right' . 'right' ≡ 'dimap' unnestRight unnestLeft . 'right'@ @since 0.0.0.0 -} class Profunctor p => Choice p where left :: p a b -> p (Either a c) (Either b c) right :: p a b -> p (Either c a) (Either c b) -- | @since 0.0.0.0 instance Choice (->) where left :: (a -> b) -> Either a c -> Either b c left ab = \case Left a -> Left $ ab a Right c -> Right c {-# INLINE left #-} right :: (a -> b) -> Either c a -> Either c b right ab = \case Right a -> Right $ ab a Left c -> Left c {-# INLINE right #-} -- | @since 0.0.0.0 instance (Applicative m) => Choice (Fun m) where left :: Fun m a b -> Fun m (Either a c) (Either b c) left (Fun amb)= Fun $ \eitherAc -> case eitherAc of Left a -> Left <$> amb a Right c -> pure $ Right c {-# INLINE left #-} right :: Fun m a b -> Fun m (Either c a) (Either c b) right (Fun amb)= Fun $ \eitherCa -> case eitherCa of Right a -> Right <$> amb a Left c -> pure $ Left c {-# INLINE right #-} {- | 'Monoidal' is 'Strong' 'Profunctor' that can be appended. It is similar to 'Monoid's but for higher-kinded types. Instances of 'Monoidal' should satisfy the following laws: * __Right identity:__ @'pappend' f 'pempty' ≡ 'first' f@ * __Left identity:__ @'pappend' 'pempty' f ≡ 'second' f@ * __Associativity:__ @'pappend' f ('pappend' g h) ⋍ 'pappend' ('pappend' f g) h@ ⚠️ __Note:__ The @⋍@ operator in the __associativity__ law is equality ignoring the structure. The law is written in that way because 'pappend' returns a tuple and the order of nested tuples depends on the order of 'pappend' applications. In practice, this means, that if you want to check the law, you reorder tuples in the following way: @ 'pappend' f ('pappend' g h) ≡ 'dimap' (\\(a, (b, c)) -> ((a, b), c)) (\\((a, b), c) -> (a, (b, c))) ('pappend' ('pappend' f g) h) @ @since 0.0.0.0 -} class Strong p => Monoidal p where pappend :: p a b -> p c d -> p (a, c) (b, d) pempty :: p a a -- | @since 0.0.0.0 instance Monoidal (->) where pappend :: (a -> b) -> (c -> d) -> (a, c) -> (b, d) pappend ab cd (a, c) = (ab a, cd c) {-# INLINE pappend #-} pempty :: a -> a pempty = id {-# INLINE pempty #-} -- | @since 0.0.0.0 instance (Applicative m) => Monoidal (Fun m) where pappend :: Fun m a b -> Fun m c d -> Fun m (a, c) (b, d) pappend (Fun amb) (Fun cmd) = Fun (\(a, c) -> liftA2 (,) (amb a) (cmd c)) {-# INLINE pappend #-} pempty :: Fun m a a pempty = Fun (pure . id) {-# INLINE pempty #-} {- | 'Optic' takes a connection from @a@ to @b@ (represented as a value of type @p a b@) and returns a connection from @source@ to @target@ (represented as a value of type @p source target@). @ +---> Profunctor | | +----> Final input | | | | +-> Final output | | | + + + type Optic p source target a b + + | | Given input <--+ | | Given output <-------+ @ @since 0.0.0.0 -} type Optic p source target a b = p a b -> p source target {- $lenses == Example To understand better how to use this library lets look at some simple example. Let's say we have the user and address data types in our system: >>> :{ data Address = Address { addressCountry :: String , addressCity :: String , addressIndex :: String } deriving (Show) :} >>> :{ data User = User { userName :: String , userAge :: Int , userAddress :: Address } deriving (Show) :} We can easily get fields of the @User@ and @Address@ types, but setting values is inconvenient (especially for nested records). To solve this problem, we can use lenses — composable getters and setters. 'Lens' is a value, so we need to define lenses for fields of our data types first. To create the lens for the @userName@ field we can use 'lens' function and manually writing getter and setter function: >>> :{ nameL :: Lens' User String nameL = lens getter setter where getter :: User -> String getter = userName setter :: User -> String -> User setter user newName = user {userName = newName} :} In this manner, we can create other lenses for our @User@ data type. Usually, lenses are one-liners, and we can define them easily using lambda-functions. >>> :{ ageL :: Lens' User Int ageL = lens userAge (\u new -> u {userAge = new}) :} >>> :{ addressL :: Lens' User Address addressL = lens userAddress (\u new -> u {userAddress = new}) :} We want to have lenses for accessing @Adress@ fields inside @User@, so we want to have the following values: @ countryL :: 'Lens'' User 'String' cityL :: 'Lens'' User 'String' indexL :: 'Lens'' User 'String' @ /Note:/ for lenses as @countryL@, @cityL@ etc., we are using composition of the lenses for the @userAddress@ field. If we have >>> :{ addressCityL :: Lens' Address String addressCityL = lens addressCity (\a new -> a {addressCity = new}) :} then >>> cityL = addressL . addressCityL Let's say we have some sample user >>> :{ address = Address { addressCountry = "UK" , addressCity = "London" , addressIndex = "XXX" } user :: User user = User { userName = "John" , userAge = 42 , userAddress = address } :} 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 {userName = "Johnny", userAge = 42, userAddress = Address {addressCountry = "UK", addressCity = "London", addressIndex = "XXX"}} >>> user & cityL .~ "Bristol" User {userName = "John", userAge = 42, userAddress = Address {addressCountry = "UK", addressCity = "Bristol", addressIndex = "XXX"}} '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 {userName = "John", userAge = 43, userAddress = Address {addressCountry = "UK", addressCity = "London", addressIndex = "XXX"}} >>> user & ageL %~ succ User {userName = "John", userAge = 43, userAddress = Address {addressCountry = "UK", addressCity = "London", addressIndex = "XXX"}} -} {- | 'Lens' represents composable getters and setters. 'Lens' is an @'Optic' p@ with the 'Strong' constraint on the @p@ type variable. @ +---> Current object | | +-> Final object | | + + type Lens source target a b + + | | Current field <--+ | | Final field <-------+ @ @since 0.0.0.0 -} type Lens source target a b = forall p . Strong p => Optic p source target a b {- | The monomorphic lenses which don't change the type of the container (or of the value inside). It has a 'Strong' constraint, and it can be used whenever a getter or a setter is needed. * @a@ is the type of the value inside of structure * @source@ is the type of the whole structure For most use-cases it's enought to use this 'Lens'' instead of more general 'Lens'. @since 0.0.0.0 -} type Lens' source a = Lens source source a a {- | Sets the given value to the structure using a setter. @since 0.0.0.0 -} set :: (p ~ (->)) => Optic p source target a b -- ^ 'Optic' that can be lens -> b -- ^ Value to set -> source -- ^ Object where we want to set value -> target -- ^ Resulting object with @b@ set set abst = abst . const {-# INLINE set #-} {- | Applies the given function to the target. @since 0.0.0.0 -} over :: (p ~ (->)) => Optic p source target a b -- ^ 'Optic' that can be lens -> (a -> b) -- ^ Field modification function -> source -- ^ Object where we want to set value -> target -- ^ Resulting object with the modified field over = id {-# INLINE over #-} {- | Gets a value out of a structure using a getter. @since 0.0.0.0 -} view :: (p ~ Fun (Const a)) => Optic p source target a b -- ^ 'Optic' that can be lens -> source -- ^ Object from which we want to get value -> a -- ^ Field of @source@ view opt = coerce (opt (Fun Const)) {-# INLINE view #-} -- view opt = getConst . unFun (opt (Fun Const)) -- opt :: Fun (Const a) a b -> Fun (Const a) s t -- opt :: (a -> Const a b) -> ( s -> Const a t) {- | Creates 'Lens' from the getter and setter. @since 0.0.0.0 -} lens :: (source -> a) -- ^ Getter -> (source -> b -> target) -- ^ Setter -> Lens source target a b lens getter setter = dimap (\s -> (s, getter s)) (uncurry setter) . second {-# INLINE lens #-} {- | The operator form of 'view' with the arguments flipped. @since 0.0.0.0 -} infixl 8 ^. (^.) :: source -> Lens' source a -> a s ^. l = view l s {-# INLINE (^.) #-} {- | The operator form of 'set'. @since 0.0.0.0 -} infixr 4 .~ (.~) :: Lens' source a -> a -> source -> source (.~) l = set l {-# INLINE (.~) #-} {- | The operator form of 'over'. @since 0.0.0.0 -} infixr 4 %~ (%~) :: Lens' source a -> (a -> a) -> source -> source (%~) l = over l {-# INLINE (%~) #-} {- | 'Lens'' for a tuple on the first argument. >>> view fstL (42, "str") 42 @since 0.0.0.0 -} fstL :: Lens (a, c) (b, c) a b fstL = lens fst $ \(_, b) new -> (new, b) {-# INLINE fstL #-} {- | 'Lens'' for a tuple on the second argument. >>> view sndL (42, "Hello") "Hello" @since 0.0.0.0 -} sndL :: Lens (x, a) (x, b) a b sndL = lens snd $ \(a, _) new -> (a, new) {-# INLINE sndL #-} {- $prisms Prisms work with sum types. == Example Let's say we have the user data type in our system: >>> :{ data Address = Address { addressCountry :: String , addressCity :: String } deriving (Show) :} >>> :{ data Payload = NamePayload String | IdPayload Int | AddressPayload Address deriving (Show) :} To create the prism for each constructor we can use 'prism'' function and manually writing getter and setter function: /NOTE:/ The naming convention for prisms is the following: @ _ConstructorName @ >>> :{ _NamePayload :: Prism' Payload String _NamePayload = prism' construct match where match :: Payload -> Maybe String match p = case p of NamePayload name -> Just name _otherPayload -> Nothing construct :: String -> Payload construct = NamePayload :} In this manner, we can create other prisms for our @Payload@ data type. >>> :{ _IdPayload :: Prism' Payload Int _IdPayload = prism' IdPayload $ \p -> case p of IdPayload i -> Just i _otherPayload -> Nothing :} >>> :{ _AddressPayload :: Prism' Payload Address _AddressPayload = prism' AddressPayload $ \p -> case p of AddressPayload a -> Just a _otherPayload -> Nothing :} Let's say we have some sample payload >>> :{ payloadName :: Payload payloadName = NamePayload "Some name" :} To view the fields of the @Payload@ data type we can use 'preview' >>> preview _NamePayload payloadName Just "Some name" >>> preview _IdPayload payloadName Nothing If we want to change any of the data, we should use 'set' or '.~' (just like in lenses) >>> set _NamePayload "Johnny" payloadName NamePayload "Johnny" >>> set _IdPayload 3 payloadName NamePayload "Some name" Note, that you can easily compose lenses and prisms together: >>> :{ address = Address { addressCountry = "UK" , addressCity = "London" } :} >>> :{ addressCityL :: Lens' Address String addressCityL = lens addressCity (\a new -> a {addressCity = new}) :} >>> :{ payloadAddress :: Payload payloadAddress = AddressPayload address :} >>> set _AddressPayload (address & addressCityL .~ "Bristol") payloadAddress AddressPayload (Address {addressCountry = "UK", addressCity = "Bristol"}) -} {- | 'Prism' represents composable constructors and deconstructors. 'Prism' is an @'Optic' p@ with 'Choice' constraint on the @p@ type variable. @ +---> Current object | | +-> Final object | | + + type Prism source target a b + + | | Field in current constructor <--+ | | Field in final constructor <-------+ @ @since 0.0.0.0 -} type Prism source target a b = forall p . Choice p => Optic p source target a b {- | The monomorphic prisms which don't change the type of the container (or of the value inside). * @a@ is the value inside the particular constructor * @source@ is some sum type @since 0.0.0.0 -} type Prism' source a = Prism source source a a {- | Newtype around function @a -> r@. It's called /forget/ because it forgets about its last type variable. @since 0.0.0.0 -} newtype Forget r a b = Forget { unForget :: a -> Maybe r } -- | @since 0.0.0.0 instance Functor (Forget r x) where fmap :: (a -> b) -> Forget r x a -> Forget r x b fmap _ = coerce {-# INLINE fmap #-} -- | @since 0.0.0.0 instance Profunctor (Forget r) where dimap :: (a -> b) -> (c -> d) -> Forget r b c -> Forget r a d dimap ab _cd (Forget br) = Forget (br . ab) {-# INLINE dimap #-} -- | @since 0.0.0.0 instance Strong (Forget r) where first :: Forget r a b -> Forget r (a, c) (b, c) first (Forget ar) = Forget (ar . fst) {-# INLINE first #-} second :: Forget r a b -> Forget r (c, a) (c, b) second (Forget ar) = Forget (ar . snd) {-# INLINE second #-} -- | @since 0.0.0.0 instance Choice (Forget r) where left :: Forget r a b -> Forget r (Either a c) (Either b c) left (Forget ar) = Forget (either ar (const Nothing)) {-# INLINE left #-} right :: Forget r a b -> Forget r (Either c a) (Either c b) right (Forget ar) = Forget (either (const Nothing) ar) {-# INLINE right #-} -- | @since 0.0.0.0 instance Monoidal (Forget r) where pappend :: Forget r a b -> Forget r c d -> Forget r (a, c) (b, d) pappend (Forget ar) (Forget cr) = Forget (\(a, c) -> getFirst $ First (ar a) <> First (cr c)) {-# INLINE pappend #-} pempty :: Forget r a a pempty = Forget (const Nothing) {-# INLINE pempty #-} {- | Match a value from @source@ type. @since 0.0.0.0 -} preview :: forall a source p . (p ~ Forget a) => Optic p source source a a -- ^ 'Optic' that can be prism -> source -- ^ Object (possible sum type) -> Maybe a -- ^ Value of type @a@ from a specific constructor preview paapss = coerce (paapss wrap) where wrap :: Forget a a a wrap = coerce @(a -> Maybe a) @(Forget a a a) Just {-# INLINE wrap #-} {-# INLINE preview #-} -- preview paapss = getFirst . unForget (paapss (Forget (First . Just))) -- paapss :: Forget (First a) a a -> Forget (First a) source source -- paapss :: (a -> First a) -> source -> First a -- paapss :: (a -> Maybe a) -> source -> Maybe a {- | Create 'Prism' from constructor and matching function. @since 0.0.0.0 -} prism :: (b -> target) -- ^ Constructor -> (source -> Either target a) -- ^ Matching function -> Prism source target a b -- prism :: (b -> target) -> (source -> Either target a) -> p a b -> p source target prism ctor match = dimap match (either id ctor) . right {-# INLINE prism #-} {- | Create monomorphic 'Prism'' from constructor and matching function. @since 0.0.0.0 -} prism' :: (a -> source) -- ^ Constructor -> (source -> Maybe a) -- ^ Matching function -> Prism' source a prism' ctor match = prism ctor (\s -> maybe (Left s) Right $ match s) {-# INLINE prism' #-} {- | 'Prism' for a 'Just' of 'Maybe'. >>> preview _Just (Just 42) Just 42 >>> preview _Just Nothing Nothing @since 0.0.0.0 -} _Just :: Prism (Maybe a) (Maybe b) a b _Just = prism Just $ \case Just a -> Right a Nothing -> Left Nothing {-# INLINE _Just #-} {- | 'Prism' for a 'Left' of 'Either'. >>> preview _Left (Left 42) Just 42 >>> preview _Left (Right "Hello") Nothing @since 0.0.0.0 -} _Left :: Prism (Either a x) (Either b x) a b _Left = prism Left $ \case Left l -> Right l Right r -> Left $ Right r {-# INLINE _Left #-} {- | 'Prism' for a 'Left' of 'Either'. >>> preview _Right (Left 42) Nothing >>> preview _Right (Right "Hello") Just "Hello" @since 0.0.0.0 -} _Right :: Prism (Either x a) (Either x b) a b _Right = prism Right $ \case Right a -> Right a Left x -> Left $ Left x {-# INLINE _Right #-} {- | 'Traversal' provides composable ways to visit different parts of a data structure. 'Traversal' is an @'Optic' p@ with the 'Choice' and 'Monoidal' constraints on the @p@ type variable. @ +---> Current collection | | +-> Final collection | | + + type Traversal source target a b + + | | Current element <--+ | | Final element <-------+ @ @since 0.0.0.0 -} type Traversal source target a b = forall p . (Choice p, Monoidal p) => Optic p source target a b {- | Traverse a data structure using given 'Traversal'. >>> traverseOf eachPair putStrLn ("Hello", "World!") Hello World! ((),()) @since 0.0.0.0 -} traverseOf :: (Applicative f, p ~ Fun f) => Optic p source target a b -- ^ 'Optic' that can be a traversal -> (a -> f b) -- ^ Traversing function -> source -- ^ Data structure to traverse -> f target -- ^ Traversing result traverseOf pabPst aFb = unFun (pabPst (Fun aFb)) -- pabPst :: Fun f a b -> Fun f source target -- pabPst :: (a -> f b) -> Fun f source target {- | 'Traversal' for a pair of same type elements. >>> over eachPair (+ 1) (3, 7) (4,8) @since 0.0.0.0 -} eachPair :: Traversal (a, a) (b, b) a b eachPair pab = pappend pab pab {- | 'Traversal' for a 'Maybe'. >>> over eachMaybe (+ 1) (Just 3) Just 4 >>> over eachMaybe (+ 1) Nothing Nothing @since 0.0.0.0 -} eachMaybe :: Traversal (Maybe a) (Maybe b) a b eachMaybe pab = dimap maybeToEither eitherToMaybe (left pab) where maybeToEither :: Maybe a -> Either a () maybeToEither = \case Just a -> Left a Nothing -> Right () eitherToMaybe :: Either a () -> Maybe a eitherToMaybe = \case Left a -> Just a Right () -> Nothing {- | 'Traversal' for lists. >>> over eachList (+ 1) [1..5] [2,3,4,5,6] >>> over eachList (+ 1) [] [] @since 0.0.0.0 -} eachList :: Traversal [a] [b] a b eachList pab = dimap listToEither eitherToList $ left $ pappend pab (eachList pab) where listToEither :: [a] -> Either (a, [a]) () listToEither = \case [] -> Right () x:xs -> Left (x, xs) eitherToList :: Either (a, [a]) () -> [a] eitherToList = \case Right () -> [] Left (x, xs) -> x:xs