lens-3.7: Lenses, Folds and Traversals

Portabilitynon-portable
Stabilityprovisional
MaintainerEdward Kmett <ekmett@gmail.com>
Safe HaskellTrustworthy

Control.Lens.Projection

Contents

Description

 

Synopsis

Projections

type Projection s t a b = forall r. (Projective r, S r ~ s, T r ~ t, A r ~ a, B r ~ b) => rSource

A Projection l is a 0-or-1 target Traversal that can also be turned around with remit to obtain a Getter in the opposite direction, such that in addition to the Traversal laws, we also have

x ^. remit l ^? l ≡ Just x
lengthOf l x <= 1

It may help to think of this as a Iso that can be partial in one direction.

Every Projection is a valid Traversal.

Every Iso is a valid Projection.

For example, you might have a Simple Projection Integer Natural allows you to always go from a Natural to an Integer, and provide you with tools to check if an Integer is a Natural and/or to edit one if it is.

 nat :: Simple Projection Integer Natural
 nat = projected toInteger $ \ i ->
    if i < 0
    then Left i
    else Right (fromInteger i)

Now we can ask if an Integer is a Natural.

>>> 5^?nat
Just 5
>>> (-5)^?nat
Nothing

We can update the ones that are:

>>> (-3,4) & both.nat *~ 2
(-3,8)

And we can then convert from a Natural to an Integer.

>>> 5 ^. remit nat -- :: Natural
5

Similarly we can use a Projection to traverse the left half of an Either:

>>> Left "hello" & _left %~ length
Left 5

or to construct an Either:

>>> 5^.remit _left
Left 5

such that if you query it with the Projection, you will get your original input back.

>>> 5^.remit _left ^? _left
Just 5

Another interesting way to think of a Projection is as the categorical dual of a Lens a co-Lens, so to speak. This is what permits the construction of outside.

Constructing Projections

class Isomorphic r => Projective r whereSource

Used to provide overloading of projections.

An instance of Projective is a Category with a canonical mapping to it from the category of embedding-projection pairs over Haskell types.

Methods

projected :: (B r -> T r) -> (S r -> Either (T r) (A r)) -> rSource

Build a Projection.

Either t a is used instead of Maybe a to permit the types of s and t to differ.

Instances

(Applicative f, ~ * x (a -> f b), ~ * y (s -> f t)) => Projective (x -> y) 
Projective (Projected s t a b) 

Consuming Projections

cloneProjection :: Projected s t a b -> Projection s t a bSource

Clone a Projection so that you can reuse the same monomorphically typed Projection for different purposes.

See cloneLens and cloneTraversal for examples of why you might want to do this.

remit :: Projected s t a b -> Getter b tSource

Turn a Projection or Iso around to build a Getter.

If you have an Iso, from is a more powerful version of this function that will return an Iso instead of a mere Getter.

>>> 5 ^.remit _left
Left 5
 remit :: Projection s t a b -> Getter b t
 remit :: Iso s t a b        -> Getter b t

review :: MonadReader b m => Projected s t a b -> m tSource

This can be used to turn an Iso or Projection around and view a value (or the current environment) through it the other way.

reviewview . remit
>>> review _left "mustard"
Left "mustard"

Usually review is used in the (->) monad with a Simple Projection or Iso, in which case it may be useful to think of it as having one of these more restricted type signatures:

 review :: Simple Iso s a        -> a -> s
 review :: Simple Projection s a -> a -> s

However, when working with a monad transformer stack, it is sometimes useful to be able to review the current environment, in which case one of these more slightly more liberal type signatures may be beneficial to think of it as having:

 review :: MonadReader a m => Simple Iso s a        -> m s
 review :: MonadReader a m => Simple Projection s a -> m s

reviews :: MonadReader b m => Projected s t a b -> (t -> r) -> m rSource

This can be used to turn an Iso or Projection around and view a value (or the current environment) through it the other way, applying a function.

reviewsviews . remit
>>> reviews _left isRight "mustard"
False

Usually this function is used in the (->) monad with a Simple Projection or Iso, in which case it may be useful to think of it as having one of these more restricted type signatures:

 reviews :: Simple Iso s a        -> (s -> r) -> a -> r
 reviews :: Simple Projection s a -> (s -> r) -> a -> r

However, when working with a monad transformer stack, it is sometimes useful to be able to review the current environment, in which case one of these more slightly more liberal type signatures may be beneficial to think of it as having:

 reviews :: MonadReader a m => Simple Iso s a        -> (s -> r) -> m r
 reviews :: MonadReader a m => Simple Projection s a -> (s -> r) -> m r

reuse :: MonadState b m => Projected s t a b -> m tSource

This can be used to turn an Iso or Projection around and use a value (or the current environment) through it the other way.

reuseuse . remit
>>> evalState (reuse _left) 5
Left 5
 reuse :: MonadState a m => Simple Projection s a -> m s
 reuse :: MonadState a m => Simple Iso s a        -> m s

reuses :: MonadState b m => Projected s t a b -> (t -> r) -> m rSource

This can be used to turn an Iso or Projection around and use the current state through it the other way, applying a function.

reusesuses . remit
>>> evalState (reuses _left isLeft) (5 :: Int)
True
 reuses :: MonadState a m => Simple Projection s a -> (s -> r) -> m r
 reuses :: MonadState a m => Simple Iso s a        -> (s -> r) -> m r

outside :: Projected s t a b -> Lens (t -> r) (s -> r) (b -> r) (a -> r)Source

Use a Projection as a kind of first-class pattern.

outside :: Projection s t a b -> Lens (t -> r) (s -> r) (b -> r) (a -> r)

aside :: Projected s t a b -> Projection (e, s) (e, t) (e, a) (e, b)Source

Use a Projection to work over part of a structure.

without :: Projected s t a b -> Projected u v c d -> Projection (Either s u) (Either t v) (Either a c) (Either b d)Source

Given a pair of projections, project sums.

Viewing a Projection as a co-lens, this combinator can be seen to be dual to alongside.

Common projections

_left :: Projection (Either a c) (Either b c) a bSource

This projection provides a traversal for tweaking the left-hand value of an Either:

>>> over _left (+1) (Left 2)
Left 3
>>> over _left (+1) (Right 2)
Right 2
>>> Right 42 ^._left :: String
""
>>> Left "hello" ^._left
"hello"

It also can be turned around to obtain the embedding into the Left half of an Either

>>> 5^.remit _left
Left 5

_right :: Projection (Either c a) (Either c b) a bSource

traverse the right-hand value of an Either:

_righttraverse

Unfortunately the instance for Traversable (Either c) is still missing from base, so this can't just be traverse

>>> over _right (+1) (Left 2)
Left 2
>>> over _right (+1) (Right 2)
Right 3
>>> Right "hello" ^._right
"hello"
>>> Left "hello" ^._right :: [Double]
[]

It also can be turned around to obtain the embedding into the Left half of an Either

>>> 5^.remit _right
Right 5

Simple