Skip to content

Commit 2824a73

Browse files
authored
Add some more optics (#185)
This commit adds the following new optics to the microlens package: * `mapMOf`, `rewriteMOf`, `transformMOf` These functions allow you to map/rewrite/transform a value using a lens a monadic action. * `anyOf`, `allOf`, `noneOf` In addition to those folds the `foldMapOf` function which was already present in the internal module is exposed by the public API. * `cosmosOf` Provided a traversal of the immediate children of a value, transitively traverse a children and the value itself. In addition to that the `Zoomed` type family is exposed by the public API of the microlens-mtl package. The changes were motivated by an issue of the Dhall project; See dhall-lang/dhall-haskell#998
1 parent f0a5354 commit 2824a73

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

microlens-mtl/src/Lens/Micro/Mtl.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module Lens.Micro.Mtl
3939
(<<%=), (<<.=),
4040

4141
-- * Zooming
42+
Zoomed,
4243
zoom,
4344
magnify,
4445
)

microlens/src/Lens/Micro.hs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ module Lens.Micro
4646
(?~),
4747
(<%~), (<<%~), (<<.~),
4848
mapped,
49+
mapMOf,
4950
rewriteOf,
51+
rewriteMOf,
5052
transformOf,
53+
transformMOf,
5154

5255
-- * Getter: extracts a value from a structure
5356
-- $getters-note
@@ -67,6 +70,10 @@ module Lens.Micro
6770
has,
6871
folded,
6972
folding,
73+
foldMapOf,
74+
anyOf,
75+
allOf,
76+
noneOf,
7077

7178
-- * Lens: a combined getter-and-setter
7279
-- $lenses-note
@@ -95,6 +102,7 @@ module Lens.Micro
95102
_head, _tail, _init, _last,
96103
mapAccumLOf,
97104
worded, lined,
105+
cosmosOf,
98106

99107
-- * Prism: a traversal iterating over at most 1 element
100108
-- $prisms-note
@@ -459,6 +467,16 @@ mapped :: Functor f => ASetter (f a) (f b) a b
459467
mapped = sets fmap
460468
{-# INLINE mapped #-}
461469

470+
{- |
471+
Map each element of a structure targeted by a Lens to a monadic action, evaluate these actions from left to right, and collect the results.
472+
473+
>>> mapMOf both (\x -> [x, x + 1]) (1,3)
474+
[(1,3),(1,4),(2,3),(2,4)]
475+
-}
476+
mapMOf :: LensLike (WrappedMonad m) s t a b -> (a -> m b) -> s -> m t
477+
mapMOf = coerce
478+
{-# INLINE mapMOf #-}
479+
462480
{- |
463481
This is a version of ('%~') which modifies the structure and returns it along with the new value:
464482
@@ -532,6 +550,17 @@ rewriteOf l f = go where
532550
go = transformOf l (\x -> maybe x go (f x))
533551
{-# INLINE rewriteOf #-}
534552

553+
{- |
554+
Rewrite by applying a monadic rule everywhere you can. Ensures that the rule cannot be applied anywhere in the result.
555+
-}
556+
rewriteMOf
557+
:: Monad m
558+
=> LensLike (WrappedMonad m) a b a b -> (b -> m (Maybe a)) -> a -> m b
559+
rewriteMOf l f = go
560+
where
561+
go = transformMOf l (\x -> f x >>= maybe (return x) go)
562+
{-# INLINE rewriteMOf #-}
563+
535564
{- |
536565
Transform every element by recursively applying a given 'ASetter' in a bottom-up manner.
537566
@@ -542,6 +571,16 @@ transformOf l f = go where
542571
go = f . over l go
543572
{-# INLINE transformOf #-}
544573

574+
{- |
575+
Transform every element by recursively applying a given 'ASetter' in a bottom-up manner with a monadic effect.
576+
-}
577+
transformMOf
578+
:: Monad m => LensLike (WrappedMonad m) a b a b -> (b -> m b) -> a -> m b
579+
transformMOf l f = go
580+
where
581+
go t = mapMOf l go t >>= f
582+
{-# INLINE transformMOf #-}
583+
545584
-- Getting -----------------------------------------------------------------
546585

547586
{- $getters-note
@@ -777,6 +816,51 @@ folding :: F.Foldable f => (s -> f a) -> SimpleFold s a
777816
folding sfa agb = phantom . F.traverse_ agb . sfa
778817
{-# INLINE folding #-}
779818

819+
{- |
820+
Returns 'True' if any value returned by a getter (any getter, including lenses,
821+
traversals, and folds) satisfies a predicate.
822+
823+
>>> anyOf each (=='x') ['x','x']
824+
True
825+
>>> anyOf each (=='x') ['x','y']
826+
True
827+
>>> anyOf each (=='x') ['y','y']
828+
False
829+
-}
830+
anyOf :: Getting Any s a -> (a -> Bool) -> s -> Bool
831+
anyOf l f = getAny #. foldMapOf l (Any #. f)
832+
{-# INLINE anyOf #-}
833+
834+
{- |
835+
Returns 'True' if any value returned by a getter (any getter, including lenses,
836+
traversals, and folds) satisfies a predicate.
837+
838+
>>> allOf each (=='x') ['x','x']
839+
True
840+
>>> allOf each (=='x') ['x','y']
841+
False
842+
>>> allOf each (=='x') ['y','y']
843+
False
844+
-}
845+
allOf :: Getting All s a -> (a -> Bool) -> s -> Bool
846+
allOf l f = getAll #. foldMapOf l (All #. f)
847+
{-# INLINE allOf #-}
848+
849+
{- |
850+
Returns 'True' if no value returned by a getter (any getter, including lenses,
851+
traversals, and folds) satisfies a predicate.
852+
853+
>>> noneOf each (=='x') ['x','x']
854+
False
855+
>>> noneOf each (=='x') ['x','y']
856+
False
857+
>>> noneOf each (=='x') ['y','y']
858+
True
859+
-}
860+
noneOf :: Getting Any s a -> (a -> Bool) -> s -> Bool
861+
noneOf l f = not . anyOf l f
862+
{-# INLINE noneOf #-}
863+
780864
-- Lenses ------------------------------------------------------------------
781865

782866
{- $lenses-note
@@ -1299,6 +1383,13 @@ lined :: Traversal' String String
12991383
lined f = fmap (intercalate "\n") . traverse f . lines
13001384
{-# INLINE lined #-}
13011385

1386+
{- |
1387+
Given a Traversal that knows how to locate immediate children, traverse all of the transitive descendants of a node, including itself.
1388+
-}
1389+
cosmosOf :: Traversal a t a t -> Traversal a t a b'
1390+
cosmosOf d f s = f s *> d (cosmosOf d f) s
1391+
{-# INLINE cosmosOf #-}
1392+
13021393
-- Prisms ------------------------------------------------------------------
13031394

13041395
{- $prisms-note

0 commit comments

Comments
 (0)