Control.Lens.Plated

lens-4.13.2.1: Lenses, Folds and Traversals

Copyright | (C) 2012-16 Edward Kmett
License | BSD-style (see the file LICENSE)
Maintainer | Edward Kmett <ekmett@gmail.com>
Stability | provisional
Portability | Rank2Types
Safe Haskell | Trustworthy
Language | Haskell98

Control.Lens.Plated

Contents

- Uniplate
- Uniplate Combinators
- Compos
- Parts
- Generics

Description

The name "plate" stems originally from "boilerplate", which was the term used by the "Scrap Your Boilerplate" papers, and later inherited by Neil Mitchell's "Uniplate".

http://community.haskell.org/~ndm/uniplate/

The combinators in here are designed to be compatible with and subsume the `uniplate` API with the notion of a `Traversal` replacing a `uniplate` or `biplate`.

By implementing these combinators in terms of `plate` instead of `uniplate` additional type safety is gained, as the user is no longer responsible for maintaining invariants such as the number of children they received.

Note: The `Biplate` is deliberately excluded from the API here, with the intention that you replace them with either explicit traversals, or by using the `On` variants of the combinators below with `biplate` from `Data.Data.Lens`. As a design, it forced the user into too many situations where they had to choose between correctness and ease of use, and it was brittle in the face of competing imports.

The sensible use of these combinators makes some simple assumptions. Notably, any of the `On` combinators are expecting a `Traversal`, `Setter` or `Fold` to play the role of the `biplate` combinator, and so when the types of the contents and the container match, they should be the `id` `Traversal`, `Setter` or `Fold`.

It is often beneficial to use the combinators in this module with the combinators from `Data.Data.Lens` or `GHC.Generics.Lens` to make it easier to automatically derive definitions for `plate`, or to derive custom traversals.

Synopsis

- class Plated a where
  - plate :: Traversal' a a
- children :: Plated a => a -> [a]
- rewrite :: Plated a => (a -> Maybe a) -> a -> a
- rewriteOf :: ASetter' a a -> (a -> Maybe a) -> a -> a
- rewriteOn :: Plated a => ASetter s t a a -> (a -> Maybe a) -> s -> t
- rewriteOnOf :: ASetter s t a a -> ASetter' a a -> (a -> Maybe a) -> s -> t
- rewriteM :: (Monad m, Plated a) => (a -> m (Maybe a)) -> a -> m a
- rewriteMOf :: Monad m => LensLike' (WrappedMonad m) a a -> (a -> m (Maybe a)) -> a -> m a
- rewriteMOn :: (Monad m, Plated a) => LensLike (WrappedMonad m) s t a a -> (a -> m (Maybe a)) -> s -> m t
- rewriteMOnOf :: Monad m => LensLike (WrappedMonad m) s t a a -> LensLike' (WrappedMonad m) a a -> (a -> m (Maybe a)) -> s -> m t
- universe :: Plated a => a -> [a]
- universeOf :: Getting [a] a a -> a -> [a]
- universeOn :: Plated a => Getting [a] s a -> s -> [a]
- universeOnOf :: Getting [a] s a -> Getting [a] a a -> s -> [a]
- cosmos :: Plated a => Fold a a
- cosmosOf :: (Applicative f, Contravariant f) => LensLike' f a a -> LensLike' f a a
- cosmosOn :: (Applicative f, Contravariant f, Plated a) => LensLike' f s a -> LensLike' f s a
- cosmosOnOf :: (Applicative f, Contravariant f) => LensLike' f s a -> LensLike' f a a -> LensLike' f s a
- transform :: Plated a => (a -> a) -> a -> a
- transformOf :: ASetter' a a -> (a -> a) -> a -> a
- transformOn :: Plated a => ASetter s t a a -> (a -> a) -> s -> t
- transformOnOf :: ASetter s t a a -> ASetter' a a -> (a -> a) -> s -> t
- transformM :: (Monad m, Plated a) => (a -> m a) -> a -> m a
- transformMOf :: Monad m => LensLike' (WrappedMonad m) a a -> (a -> m a) -> a -> m a
- transformMOn :: (Monad m, Plated a) => LensLike (WrappedMonad m) s t a a -> (a -> m a) -> s -> m t
- transformMOnOf :: Monad m => LensLike (WrappedMonad m) s t a a -> LensLike' (WrappedMonad m) a a -> (a -> m a) -> s -> m t
- contexts :: Plated a => a -> [Context a a a]
- contextsOf :: ATraversal' a a -> a -> [Context a a a]
- contextsOn :: Plated a => ATraversal s t a a -> s -> [Context a a t]
- contextsOnOf :: ATraversal s t a a -> ATraversal' a a -> s -> [Context a a t]
- holes :: Plated a => a -> [Pretext (->) a a a]
- holesOn :: Conjoined p => Over p (Bazaar p a a) s t a a -> s -> [Pretext p a a t]
- holesOnOf :: Conjoined p => LensLike (Bazaar p r r) s t a b -> Over p (Bazaar p r r) a b r r -> s -> [Pretext p r r t]
- para :: Plated a => (a -> [r] -> r) -> a -> r
- paraOf :: Getting (Endo [a]) a a -> (a -> [r] -> r) -> a -> r
- (...) :: (Applicative f, Plated c) => LensLike f s t c c -> Over p f c c a b -> Over p f s t a b
- deep :: (Conjoined p, Applicative f, Plated s) => Traversing p f s s a b -> Over p f s s a b
- composOpFold :: Plated a => b -> (b -> b -> b) -> (a -> b) -> a -> b
- parts :: Plated a => Lens' a [a]
- gplate :: (Generic a, GPlated a (Rep a)) => Traversal' a a
- class GPlated a g

# Uniplate

class Plated a where

A `Plated` type is one where we know how to extract its immediate self-similar children.

Example 1:

    import Control.Applicative
    import Control.Lens
    import Control.Lens.Plated
    import Data.Data
    import Data.Data.Lens (uniplate)

    data Expr
      = Val Int
      | Neg Expr
      | Add Expr Expr
      deriving (Eq,Ord,Show,Read,Data,Typeable)

    instance Plated Expr where
      plate f (Neg e) = Neg <$> f e
      plate f (Add a b) = Add <$> f a <*> f b
      plate _ a = pure a

or

    instance Plated Expr where
      plate = uniplate

Example 2:

    import Control.Applicative
    import Control.Lens
    import Control.Lens.Plated
    import Data.Data
    import Data.Data.Lens (uniplate)

    data Tree a
      = Bin (Tree a) (Tree a)
      | Tip a
      deriving (Eq,Ord,Show,Read,Data,Typeable)

    instance Plated (Tree a) where
      plate f (Bin l r) = Bin <$> f l <*> f r
      plate _ t = pure t

or

    instance Data a => Plated (Tree a) where
      plate = uniplate

Note the big distinction between these two implementations.

The former will only treat children directly in this tree as descendents, the latter will treat trees contained in the values under the tips also as descendants!

When in doubt, pick a `Traversal` and just use the various `...Of` combinators rather than pollute `Plated` with orphan instances!

If you want to find something unplated and non-recursive with `biplate` use the `...OnOf` variant with `ignored`, though those usecases are much better served in most cases by using the existing `Lens` combinators! e.g.

    toListOf biplate ≡ universeOnOf biplate ignored

This same ability to explicitly pass the `Traversal` in question is why there is no analogue to uniplate's `Biplate`.

Moreover, since we can allow custom traversals, we implement reasonable defaults for polymorphic data types, that only `traverse` into themselves, and not their polymorphic arguments.

Minimal complete definition

Nothing

Methods

plate :: Traversal' a a

`Traversal` of the immediate children of this structure.

If you're using GHC 7.2 or newer and your type has a `Data` instance, `plate` will default to `uniplate` and you can choose to not override it with your own definition.

Instances

Plated Exp |
Plated Pat |
Plated Type |
Plated Dec |
Plated Stmt |
Plated Con |
Plated [a] |
Plated (Tree a) |
Traversable f => Plated (Cofree f a) |
Traversable f => Plated (F f a) |
Traversable f => Plated (Free f a) |
(Traversable f, Traversable w) => Plated (CofreeT f w a) |
(Traversable f, Traversable m) => Plated (FreeT f m a) |

# Uniplate Combinators

children :: Plated a => a -> [a]

Extract the immediate descendants of a `Plated` container.

    children ≡ toListOf plate

rewrite :: Plated a => (a -> Maybe a) -> a -> a

Rewrite by applying a rule everywhere you can. Ensures that the rule cannot be applied anywhere in the result:

    propRewrite r x = all (isNothing . r) (universe (rewrite r x))

Usually `transform` is more appropriate, but `rewrite` can give better compositionality. Given two single transformations `f` and `g`, you can construct `a -> f a `mplus` g a` which performs both rewrites until a fixed point.

rewriteOf :: ASetter' a a -> (a -> Maybe a) -> a -> a

Rewrite by applying a rule everywhere you can. Ensures that the rule cannot be applied anywhere in the result:

    propRewriteOf l r x = all (isNothing . r) (universeOf l (rewriteOf l r x))

Usually `transformOf` is more appropriate, but `rewriteOf` can give better compositionality. Given two single transformations `f` and `g`, you can construct `a -> f a `mplus` g a` which performs both rewrites until a fixed point.

    rewriteOf :: Iso' a a       -> (a -> Maybe a) -> a -> a
    rewriteOf :: Lens' a a      -> (a -> Maybe a) -> a -> a
    rewriteOf :: Traversal' a a -> (a -> Maybe a) -> a -> a
    rewriteOf :: Setter' a a    -> (a -> Maybe a) -> a -> a

rewriteOn :: Plated a => ASetter s t a a -> (a -> Maybe a) -> s -> t

Rewrite recursively over part of a larger structure.

    rewriteOn :: Plated a => Iso' s a       -> (a -> Maybe a) -> s -> s
    rewriteOn :: Plated a => Lens' s a      -> (a -> Maybe a) -> s -> s
    rewriteOn :: Plated a => Traversal' s a -> (a -> Maybe a) -> s -> s
    rewriteOn :: Plated a => ASetter' s a   -> (a -> Maybe a) -> s -> s

rewriteOnOf :: ASetter s t a a -> ASetter' a a -> (a -> Maybe a) -> s -> t

Rewrite recursively over part of a larger structure using a specified `Setter`.

    rewriteOnOf :: Iso' s a       -> Iso' a a       -> (a -> Maybe a) -> s -> s
    rewriteOnOf :: Lens' s a      -> Lens' a a      -> (a -> Maybe a) -> s -> s
    rewriteOnOf :: Traversal' s a -> Traversal' a a -> (a -> Maybe a) -> s -> s
    rewriteOnOf :: Setter' s a    -> Setter' a a    -> (a -> Maybe a) -> s -> s

rewriteM :: (Monad m, Plated a) => (a -> m (Maybe a)) -> a -> m a

Rewrite by applying a monadic rule everywhere you can. Ensures that the rule cannot be applied anywhere in the result.

rewriteMOf :: Monad m => LensLike' (WrappedMonad m) a a -> (a -> m (Maybe a)) -> a -> m a

Rewrite by applying a monadic rule everywhere you recursing with a user-specified `Traversal`. Ensures that the rule cannot be applied anywhere in the result.

rewriteMOn :: (Monad m, Plated a) => LensLike (WrappedMonad m) s t a a -> (a -> m (Maybe a)) -> s -> m t

Rewrite by applying a monadic rule everywhere inside of a structure located by a user-specified `Traversal`. Ensures that the rule cannot be applied anywhere in the result.

rewriteMOnOf :: Monad m => LensLike (WrappedMonad m) s t a a -> LensLike' (WrappedMonad m) a a -> (a -> m (Maybe a)) -> s -> m t

Rewrite by applying a monadic rule everywhere inside of a structure located by a user-specified `Traversal`, using a user-specified `Traversal` for recursion. Ensures that the rule cannot be applied anywhere in the result.

universe :: Plated a => a -> [a]

Retrieve all of the transitive descendants of a `Plated` container, including itself.

universeOf :: Getting [a] a a -> a -> [a]

Given a `Fold` that knows how to locate immediate children, retrieve all of the transitive descendants of a node, including itself.

    universeOf :: Fold a a -> a -> [a]

universeOn :: Plated a => Getting [a] s a -> s -> [a]

Given a `Fold` that knows how to find `Plated` parts of a container retrieve them and all of their descendants, recursively.

universeOnOf :: Getting [a] s a -> Getting [a] a a -> s -> [a]

Given a `Fold` that knows how to locate immediate children, retrieve all of the transitive descendants of a node, including itself that lie in a region indicated by another `Fold`.

    toListOf l ≡ universeOnOf l ignored

cosmos :: Plated a => Fold a a

Fold over all transitive descendants of a `Plated` container, including itself.

cosmosOf :: (Applicative f, Contravariant f) => LensLike' f a a -> LensLike' f a a

Given a `Fold` that knows how to locate immediate children, fold all of the transitive descendants of a node, including itself.

    cosmosOf :: Fold a a -> Fold a a

cosmosOn :: (Applicative f, Contravariant f, Plated a) => LensLike' f s a -> LensLike' f s a

Given a `Fold` that knows how to find `Plated` parts of a container fold them and all of their descendants, recursively.

    cosmosOn :: Plated a => Fold s a -> Fold s a

cosmosOnOf :: (Applicative f, Contravariant f) => LensLike' f s a -> LensLike' f a a -> LensLike' f s a

Given a `Fold` that knows how to locate immediate children, fold all of the transitive descendants of a node, including itself that lie in a region indicated by another `Fold`.

    cosmosOnOf :: Fold s a -> Fold a a -> Fold s a

transform :: Plated a => (a -> a) -> a -> a

Transform every element in the tree, in a bottom-up manner.

For example, replacing negative literals with literals:

    negLits = transform $ \x -> case x of
      Neg (Lit i) -> Lit (negate i)
      _           -> x

transformOf :: ASetter' a a -> (a -> a) -> a -> a

Transform every element by recursively applying a given `Setter` in a bottom-up manner.

    transformOf :: Traversal' a a -> (a -> a) -> a -> a
    transformOf :: Setter' a a    -> (a -> a) -> a -> a

transformOn :: Plated a => ASetter s t a a -> (a -> a) -> s -> t

Transform every element in the tree in a bottom-up manner over a region indicated by a `Setter`.

    transformOn :: Plated a => Traversal' s a -> (a -> a) -> s -> s
    transformOn :: Plated a => Setter' s a    -> (a -> a) -> s -> s

transformOnOf :: ASetter s t a a -> ASetter' a a -> (a -> a) -> s -> t

Transform every element in a region indicated by a `Setter` by recursively applying another `Setter` in a bottom-up manner.

    transformOnOf :: Setter' s a -> Traversal' a a -> (a -> a) -> s -> s
    transformOnOf :: Setter' s a -> Setter' a a    -> (a -> a) -> s -> s

transformM :: (Monad m, Plated a) => (a -> m a) -> a -> m a

Transform every element in the tree, in a bottom-up manner, monadically.

transformMOf :: Monad m => LensLike' (WrappedMonad m) a a -> (a -> m a) -> a -> m a

Transform every element in a tree using a user supplied `Traversal` in a bottom-up manner with a monadic effect.

    transformMOf :: Monad m => Traversal' a a -> (a -> m a) -> a -> m a

transformMOn :: (Monad m, Plated a) => LensLike (WrappedMonad m) s t a a -> (a -> m a) -> s -> m t

Transform every element in the tree in a region indicated by a supplied `Traversal`, in a bottom-up manner, monadically.

    transformMOn :: (Monad m, Plated a) => Traversal' s a -> (a -> m a) -> s -> m s

transformMOnOf :: Monad m => LensLike (WrappedMonad m) s t a a -> LensLike' (WrappedMonad m) a a -> (a -> m a) -> s -> m t

Transform every element in a tree that lies in a region indicated by a supplied `Traversal`, walking with a user supplied `Traversal` in a bottom-up manner with a monadic effect.

    transformMOnOf :: Monad m => Traversal' s a -> Traversal' a a -> (a -> m a) -> s -> m s

contexts :: Plated a => a -> [Context a a a]

Return a list of all of the editable contexts for every location in the structure, recursively.

    propUniverse x = universe x == map pos (contexts x)
    propId x = all (== x) [extract w | w <- contexts x]

    contexts ≡ contextsOf plate

contextsOf :: ATraversal' a a -> a -> [Context a a a]

Return a list of all of the editable contexts for every location in the structure, recursively, using a user-specified `Traversal` to walk each layer.

    propUniverse l x = universeOf l x == map pos (contextsOf l x)
    propId l x = all (== x) [extract w | w <- contextsOf l x]

    contextsOf :: Traversal' a a -> a -> [Context a a a]

contextsOn :: Plated a => ATraversal s t a a -> s -> [Context a a t]

Return a list of all of the editable contexts for every location in the structure in an areas indicated by a user supplied `Traversal`, recursively using `plate`.

    contextsOn b ≡ contextsOnOf b plate

    contextsOn :: Plated a => Traversal' s a -> s -> [Context a a s]

contextsOnOf :: ATraversal s t a a -> ATraversal' a a -> s -> [Context a a t]

Return a list of all of the editable contexts for every location in the structure in an areas indicated by a user supplied `Traversal`, recursively using another user-supplied `Traversal` to walk each layer.

    contextsOnOf :: Traversal' s a -> Traversal' a a -> s -> [Context a a s]

holes :: Plated a => a -> [Pretext (->) a a a]

The one-level version of `context`. This extracts a list of the immediate children as editable contexts.

Given a context you can use `pos` to see the values, `peek` at what the structure would be like with an edited result, or simply `extract` the original structure.

    propChildren x = children l x == map pos (holes l x)
    propId x = all (== x) [extract w | w <- holes l x]

    holes = holesOf plate

holesOn :: Conjoined p => Over p (Bazaar p a a) s t a a -> s -> [Pretext p a a t]

An alias for `holesOf`, provided for consistency with the other combinators.

    holesOn ≡ holesOf

    holesOn :: Iso' s a                -> s -> [Pretext (->) a a s]
    holesOn :: Lens' s a               -> s -> [Pretext (->) a a s]
    holesOn :: Traversal' s a          -> s -> [Pretext (->) a a s]
    holesOn :: IndexedLens' i s a      -> s -> [Pretext (Indexed i) a a s]
    holesOn :: IndexedTraversal' i s a -> s -> [Pretext (Indexed i) a a s]

holesOnOf :: Conjoined p => LensLike (Bazaar p r r) s t a b -> Over p (Bazaar p r r) a b r r -> s -> [Pretext p r r t]

Extract one level of `holes` from a container in a region specified by one `Traversal`, using another.

    holesOnOf b l ≡ holesOf (b . l)

    holesOnOf :: Iso' s a       -> Iso' a a                -> s -> [Pretext (->) a a s]
    holesOnOf :: Lens' s a      -> Lens' a a               -> s -> [Pretext (->) a a s]
    holesOnOf :: Traversal' s a -> Traversal' a a          -> s -> [Pretext (->) a a s]
    holesOnOf :: Lens' s a      -> IndexedLens' i a a      -> s -> [Pretext (Indexed i) a a s]
    holesOnOf :: Traversal' s a -> IndexedTraversal' i a a -> s -> [Pretext (Indexed i) a a s]

para :: Plated a => (a -> [r] -> r) -> a -> r

Perform a fold-like computation on each value, technically a paramorphism.

    para ≡ paraOf plate

paraOf :: Getting (Endo [a]) a a -> (a -> [r] -> r) -> a -> r

Perform a fold-like computation on each value, technically a paramorphism.

    paraOf :: Fold a a -> (a -> [r] -> r) -> a -> r

(...) :: (Applicative f, Plated c) => LensLike f s t c c -> Over p f c c a b -> Over p f s t a b infixr 9

Compose through a plate

deep :: (Conjoined p, Applicative f, Plated s) => Traversing p f s s a b -> Over p f s s a b

Try to apply a traversal to all transitive descendants of a `Plated` container, but do not recurse through matching descendants.

    deep :: Plated s => Fold s a                 -> Fold s a
    deep :: Plated s => IndexedFold s a          -> IndexedFold s a
    deep :: Plated s => Traversal s s a b        -> Traversal s s a b
    deep :: Plated s => IndexedTraversal s s a b -> IndexedTraversal s s a b

# Compos

Provided for compatibility with Björn Bringert's `compos` library.

Note: Other operations from compos that were inherited by `uniplate` are not included to avoid having even more redundant names for the same operators. For comparison:

    composOpMonoid ≡ foldMapOf plate
    composOpMPlus f ≡ msumOf (plate . to f)
    composOp ≡ descend ≡ over plate
    composOpM ≡ descendM ≡ mapMOf plate
    composOpM_ ≡ descendM_ ≡ mapMOf_ plate

composOpFold :: Plated a => b -> (b -> b -> b) -> (a -> b) -> a -> b

Fold the immediate children of a `Plated` container.

    composOpFold z c f = foldrOf plate (c . f) z

# Parts

parts :: Plated a => Lens' a [a]

The original `uniplate` combinator, implemented in terms of `Plated` as a `Lens`.

    parts ≡ partsOf plate

The resulting `Lens` is safer to use as it ignores 'over-application' and deals gracefully with under-application, but it is only a proper `Lens` if you don't change the list `length`!

# Generics

gplate :: (Generic a, GPlated a (Rep a)) => Traversal' a a

Implement `plate` operation for a type using its `Generic` instance.

class GPlated a g

Minimal complete definition

gplate'

Instances

GPlated a V1 |
GPlated a U1 |
GPlated a (K1 i b) |
GPlated a (K1 i a) |
(GPlated a f, GPlated a g) => GPlated a ((:*:) f g) |
(GPlated a f, GPlated a g) => GPlated a ((:+:) f g) |
GPlated a f => GPlated a (M1 i c f) |