| Copyright | (C) 2013-2016 Edward Kmett 2018 Monadfix |
|---|---|
| License | BSD-style (see the file LICENSE) |
| Safe Haskell | Safe-Inferred |
| Language | Haskell2010 |
Lens.Micro.Pro
Description
This module is home to lens definitions that require
profunctors, most notably
Iso and Prism. Depending on profunctors is quite the to bear — one
that includes all dependencies of microlens-platform. For this reason,
microlens-pro ships with a compatiblity module Lens.Micro.ProCompat which
re-exports the entirety of Lens.Micro.Platform, but with the profunctor-less
definitions hidden and overridden with profunctor'd definitions from this module.
Synopsis
- type Iso s t a b = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t)
- type Iso' s a = Iso s s a a
- iso :: (s -> a) -> (b -> t) -> Iso s t a b
- from :: AnIso s t a b -> Iso b a t s
- under :: AnIso s t a b -> (t -> s) -> b -> a
- non :: Eq a => a -> Iso' (Maybe a) a
- non' :: APrism' a () -> Iso' (Maybe a) a
- _Show :: (Read a, Show a) => Iso' String a
- strict :: Strict lazy strict => Iso' lazy strict
- lazy :: Strict lazy strict => Iso' strict lazy
- enum :: Enum a => Iso' Int a
- coerced :: forall s t a b. (Coercible s a, Coercible t b) => Iso s t a b
- mapping :: (Functor f, Functor g) => AnIso s t a b -> Iso (f s) (g t) (f a) (g b)
- packed :: IsText t => Iso' String t
- unpacked :: IsText t => Iso' t String
- type AnIso s t a b = Exchange a b a (Identity b) -> Exchange a b s (Identity t)
- type AnIso' s a = AnIso s s a a
- cloneIso :: AnIso s t a b -> Iso s t a b
- withIso :: forall s t a b rep (r :: TYPE rep). AnIso s t a b -> ((s -> a) -> (b -> t) -> r) -> r
- type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
- type Prism' s a = Prism s s a a
- prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
- prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b
- nearly :: a -> (a -> Bool) -> Prism' a ()
- only :: Eq a => a -> Prism' a ()
- _Left :: Prism (Either a c) (Either b c) a b
- _Right :: Prism (Either c a) (Either c b) a b
- _Just :: Prism (Maybe a) (Maybe b) a b
- _Nothing :: Prism' (Maybe a) ()
- _Empty :: AsEmpty a => Prism' a ()
- type APrism s t a b = Market a b a (Identity b) -> Market a b s (Identity t)
- type APrism' s a = Market a a a (Identity a) -> Market a a s (Identity s)
- clonePrism :: APrism s t a b -> Prism s t a b
- withPrism :: APrism s t a b -> ((b -> t) -> (s -> Either t a) -> r) -> r
- type AReview t b = Tagged b (Identity b) -> Tagged t (Identity t)
- type SimpleReview t b = forall p. (Choice p, Bifunctor p) => p b (Identity b) -> p t (Identity t)
- re :: AReview t b -> Getter b t
- review :: MonadReader b m => AReview t b -> m t
- (#) :: AReview t b -> b -> t
- unto :: (Profunctor p, Bifunctor p, Functor f) => (b -> t) -> p a (f b) -> p s (f t)
Iso: Losslessly convert between types
Isos (or isomorphisms) are lenses that convert a value instead of targeting a
part of it; in other words, inside of every list lives a reversed list, inside
of every strict Text lives a lazy Text, and inside of every (a, b) lives a
(b, a). Since an isomorphism doesn't lose any information, it's possible to
reverse it and use it in the opposite direction by using from:
from :: Iso' s a -> Iso' a s from :: Iso s t a b -> Iso t s b a
Isos are constructed from a pair of inverse functions. For example, assume
lawful instances of Show and Read:
show . read = id read . show = id
The isomorphisms defined in this module are true lens-compatible isos. Many of them share names with the lens-incompatible definitions from Lens.Micro and Lens.Micro.Platform. For convenience, we provide a module Lens.Micro.ProCompat which emulates Lens.Micro.Platform, but uses the lens-compatible isos.
type Iso s t a b = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t) Source #
The type signature of iso provides a nice interpretation of
Iso. If you want to apply a function a -> b to a type s, you'd have to
convert with s -> a, apply your function a -> b, and convert back with
b -> t.
iso:: (s -> a) -> (b -> t) -> Iso s t a b -- or, put monomorphicallyiso:: (s -> a) -> (a -> s) -> Iso' s a
type Iso' s a = Iso s s a a Source #
The type of monomorphic isomorphisms, i.e. isos that change neither the outer type
s nor the inner type a.
Constructing Isos
Iso Combinators
non :: Eq a => a -> Iso' (Maybe a) a Source #
non lets you “relabel” a Maybe by equating Nothing to an arbitrary value
(which you can choose):
>>>Just 1 ^. non 0 1
>>>Nothing ^. non 0 0
The most useful thing about non is that relabeling also works in other
direction. If you try to set the “forbidden” value, it'll be turned to
Nothing:
>>>Just 1 & non 0 .~ 0 Nothing
Setting anything else works just fine:
>>>Just 1 & non 0 .~ 5 Just 5
Same happens if you try to modify a value:
>>>Just 1 & non 0 %~ subtract 1 Nothing
>>>Just 1 & non 0 %~ (+ 1) Just 2
non is often useful when combined with at. For instance, if you have a map
of songs and their playcounts, it makes sense not to store songs with 0 plays in
the map; non can act as a filter that wouldn't pass such entries.
Decrease playcount of a song to 0, and it'll be gone:
>>>fromList [("Soon",1),("Yesterday",3)] & at "Soon" . non 0 %~ subtract 1fromList [("Yesterday",3)]
Try to add a song with 0 plays, and it won't be added:
>>>fromList [("Yesterday",3)] & at "Soon" . non 0 .~ 0fromList [("Yesterday",3)]
But it will be added if you set any other number:
>>>fromList [("Yesterday",3)] & at "Soon" . non 0 .~ 1fromList [("Soon",1),("Yesterday",3)]
non is also useful when working with nested maps. Here a nested map is created
when it's missing:
>>>Map.empty & at "Dez Mona" . non Map.empty . at "Soon" .~ Just 1fromList [("Dez Mona",fromList [("Soon",1)])]
and here it is deleted when its last entry is deleted (notice that non is used
twice here):
>>>fromList [("Dez Mona",fromList [("Soon",1)])] & at "Dez Mona" . non Map.empty . at "Soon" . non 0 %~ subtract 1fromList []
To understand the last example better, observe the flow of values in it:
- the map goes into
at "Dez Mona"* the nested map (wrapped intoJust) goes intonon Map.empty*Justis unwrapped and the nested map goes intoat "Soon"*Just 1is unwrapped bynon 0
Then the final value – i.e. 1 – is modified by subtract 1 and the result
(which is 0) starts flowing backwards:
non 0sees the 0 and produces aNothingat "Soon"seesNothingand deletes the corresponding value from the map- the resulting empty map is passed to
non Map.empty, which sees that it's empty and thus producesNothing at "Dez Mona"seesNothingand removes the key from the map
Common Isos
enum :: Enum a => Iso' Int a Source #
enum is a questionable inclusion, as many (most) Enum instances throw
errors for out-of-bounds integers, but it is occasionally useful when used with
that information in mind. Handle with care!
>>>97 ^. enum :: Char'a'>>>(-1) ^. enum :: Char*** Exception: Prelude.chr: bad argument: (-1)>>>[True,False] ^. mapping (from enum)[1,0]
coerced :: forall s t a b. (Coercible s a, Coercible t b) => Iso s t a b Source #
Coercible types have the same runtime representation, i.e. they are isomorphic.
>>>(Sum 123 :: Sum Int) ^. coerced :: Int123
mapping :: (Functor f, Functor g) => AnIso s t a b -> Iso (f s) (g t) (f a) (g b) Source #
An isomorphism holds when lifted into a functor. For example, if a list contains
a bunch of a's which are each isomorphic to a b, the whole list of a's is
isomorphic to a list of b's.
>>>["1","2","3"] ^. mapping _Show :: [Int][1,2,3]>>>([1,2,3] :: [Int]) ^. from (mapping _Show)["1","2","3"]
This also hold across different functors:
>>>let l = mapping @[] @Maybe _Show>>>:t ll :: (Read b, Show b) => Iso [String] (Maybe String) [b] (Maybe b)>>>["1","2","3"] & l %~ Just . sumJust "6"
Miscellaneous
withIso :: forall s t a b rep (r :: TYPE rep). AnIso s t a b -> ((s -> a) -> (b -> t) -> r) -> r Source #
Extract the two functions, s -> a and one b -> t that characterize an
Iso.
Prism: A traversal with zero or one targets
If a Lens views and updates individual components of product types, a
Prism views and updates individual components of sum types. For example, you
may want to update the Left field of an Either:
>>>Left "salmon" & _Left .~ "orb"Left "orb">>>Right "pudding" & _Left .~ "orb"Right "pudding"
Also similarly to a Lens, you might want to view the Left field. However, it
might not always be there, so we treat it as a traversal with either one or zero
results.
>>>Right "bass" ^? _LeftNothing>>>Left "bubbles" ^? _LeftJust "bubbles"
A unique feature of Prisms is that they may be flipped around using re to
construct the larger structure. Maintaining our example of Either, remember
that you can construct the entire Either via the constructor Left.
>>>:t re _Leftre _Left :: Getter b (Either b c)>>>view (re _Left) "bungo"Left "bungo"
This idiom isn't the prettiest, so we define view (re l) as shorthand. review =
view . rereview also has an infix synonym, (#).
>>>:t _Just_Just :: Prism (Maybe a) (Maybe b) a b>>>review _Just "bilbo"Just "bilbo">>>_Just # "bilbo"Just "bilbo"
As is the whole point of optics, prisms may of course be composed with other optics:
type Thing = Either (Maybe String) (Maybe (Either [Bool] Int)) thing :: Thing thing = Right (Just (Left [True,False]))
>>>thing & _Right . _Just . _Left . each %~ notRight (Just (Left [False,True]))
type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t) Source #
sis the type of the whole structuretis the type of the reconstructed structureais the type of the targetbis the type of the value used for reconstruction
type Prism' s a = Prism s s a a Source #
The type of monomorphic prisms, i.e. prisms that change neither the outer type
s nor the inner type a.
Constructing Prisms
prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b Source #
Generate a Prism out of a constructor and a selector.
_Nothing = prism Left $ either Right (Left . Right)
Prism Combinators
nearly :: a -> (a -> Bool) -> Prism' a () Source #
is a prism that matches "loose equality" with nearly a pa by assuming p
x is true iff x ≡ a.
>>>nearly [] null # ()[]>>>[1,2,3,4] ^? nearly [] nullNothing
only :: Eq a => a -> Prism' a () Source #
A prism that matches equality with a value:
>>>1 ^? only 2Nothing>>>1 ^? only 1Just 1
Common Prisms
_Empty :: AsEmpty a => Prism' a () Source #
A prism that matches the empty structure.
>>>has _Empty []True
Miscellaneous
type APrism' s a = Market a a a (Identity a) -> Market a a s (Identity s) Source #
Monomorphic APrism.
clonePrism :: APrism s t a b -> Prism s t a b Source #
Clone a Prism so that you can reuse the same monomorphically typed Prism for different purposes.
Cloning a Prism is one way to make sure you aren't given something weaker,
such as a Traversal and can be used as a way to pass around lenses that have
to be monomorphic in f.
Review
type SimpleReview t b = forall p. (Choice p, Bifunctor p) => p b (Identity b) -> p t (Identity t) Source #
Review,
from lens, is limited form of Prism that can only be used for re operations.
Similarly to SimpleGetter from microlens, microlens-pro does not define Review and opts for
a less general SimpleReview in order to avoid a
distributive
dependency.
review :: MonadReader b m => AReview t b -> m t Source #
unto :: (Profunctor p, Bifunctor p, Functor f) => (b -> t) -> p a (f b) -> p s (f t) Source #
Construct a Review out of a constructor. Consider this more pleasant type
signature:
unto :: (b -> t) -> Review' t b
Pardon the actual type signature — microlens defines neither Optic (used in
lens'
unto) nor Review'. Here we simply expand the definition of Optic.