Copyright | (C) 2013-2016 Edward Kmett 2018 Monadfix |
---|---|
License | BSD-style (see the file LICENSE) |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
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 1
fromList [("Yesterday",3)]
Try to add a song with 0 plays, and it won't be added:
>>>
fromList [("Yesterday",3)] & at "Soon" . non 0 .~ 0
fromList [("Yesterday",3)]
But it will be added if you set any other number:
>>>
fromList [("Yesterday",3)] & at "Soon" . non 0 .~ 1
fromList [("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 1
fromList [("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 1
fromList []
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
*Just
is unwrapped and the nested map goes intoat "Soon"
*Just 1
is 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 0
sees the 0 and produces aNothing
at "Soon"
seesNothing
and 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"
seesNothing
and 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 :: Int
123
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 l
l :: (Read b, Show b) => Iso [String] (Maybe String) [b] (Maybe b)>>>
["1","2","3"] & l %~ Just . sum
Just "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" ^? _Left
Nothing>>>
Left "bubbles" ^? _Left
Just "bubbles"
A unique feature of Prism
s 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 _Left
re _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
. re
review
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 %~ not
Right (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 #
s
is the type of the whole structuret
is the type of the reconstructed structurea
is the type of the targetb
is 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 [] null
Nothing
only :: Eq a => a -> Prism' a () Source #
A prism that matches equality with a value:
>>>
1 ^? only 2
Nothing>>>
1 ^? only 1
Just 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
.