| Portability | non-portable |
|---|---|
| Stability | provisional |
| Maintainer | Edward Kmett <ekmett@gmail.com> |
| Safe Haskell | Trustworthy |
Control.Lens.Projection
Description
- type Projection s t a b = forall r. (Projective r, S r ~ s, T r ~ t, A r ~ a, B r ~ b) => r
- class Isomorphic r => Projective r where
- cloneProjection :: Projected s t a b -> Projection s t a b
- remit :: Projected s t a b -> Getter b t
- review :: MonadReader b m => Projected s t a b -> m t
- reviews :: MonadReader b m => Projected s t a b -> (t -> r) -> m r
- reuse :: MonadState b m => Projected s t a b -> m t
- reuses :: MonadState b m => Projected s t a b -> (t -> r) -> m r
- outside :: Projected 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)
- without :: Projected s t a b -> Projected u v c d -> Projection (Either s u) (Either t v) (Either a c) (Either b d)
- _left :: Projection (Either a c) (Either b c) a b
- _right :: Projection (Either c a) (Either c b) a b
- type SimpleProjection s a = Projection s s a a
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^.remitl^?l ≡Justx
lengthOfl 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 allows you to always
go from a Simple Projection Integer NaturalNatural 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::SimpleProjectionIntegerNaturalnat=projectedtoInteger$\ i -> if i<0 thenLefti elseRight(fromIntegeri)
Now we can ask if an Integer is a Natural.
>>>5^?natJust 5
>>>(-5)^?natNothing
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 -- :: Natural5
Similarly we can use a Projection to traverse the left half of an Either:
>>>Left "hello" & _left %~ lengthLeft 5
or to construct an Either:
>>>5^.remit _leftLeft 5
such that if you query it with the Projection, you will get your original input back.
>>>5^.remit _left ^? _leftJust 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.
is used instead of Either t a to permit the types of Maybe as 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.
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.
review≡view.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::SimpleIsos a -> a -> sreview::SimpleProjections 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::MonadReadera m =>SimpleIsos a -> m sreview::MonadReadera m =>SimpleProjections 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.
reviews≡views.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::SimpleIsos a -> (s -> r) -> a -> rreviews::SimpleProjections 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::MonadReadera m =>SimpleIsos a -> (s -> r) -> m rreviews::MonadReadera m =>SimpleProjections a -> (s -> r) -> m r
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.
reuses≡uses.remit
>>>evalState (reuses _left isLeft) (5 :: Int)True
reuses::MonadStatea m =>SimpleProjections a -> (s -> r) -> m rreuses::MonadStatea m =>SimpleIsos 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::Projections 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 _leftLeft 5
_right :: Projection (Either c a) (Either c b) a bSource
traverse the right-hand value of an Either:
_right≡traverse
Unfortunately the instance for
is still missing from base,
so this can't just be Traversable (Either c)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 _rightRight 5
Simple
type SimpleProjection s a = Projection s s a aSource
A .
Simple Projection