HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Modulelens-5.3.5Haskell2010

Control.Lens.Plated

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".

https://www.cs.york.ac.uk/fp/darcs/uniplate/uniplate.htm

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.

  • 3 classes
  • 40 values
  • Packagelens-5.3.5
  • Exports43
  • LanguageHaskell2010
  • LicenceBSD-2-Clause
  • SourcePlated.hs

Uniplate

1 declaration
classclass 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)
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)
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 Control.Traversable.traverse into themselves, and not their polymorphic arguments.

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.

Instances13Plated, …

Uniplate Combinators

36 declarations
valuerewrite :: 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 (Data.Just.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 <|> g a which performs both rewrites until a fixed point.

valuerewriteOf :: ASetter a b a b -> (b -> Maybe a) -> a -> b
#

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

propRewriteOf l r x = all (Data.Just.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 <|> g a which performs both rewrites until a fixed point.

rewriteOf :: Control.Lens.Iso.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
valuerewriteM :: (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.

valuerewriteMOf
  1. :: Monad m
  2. => LensLike (WrappedMonad m) a b a b
  3. -> b -> m (Maybe a)
  4. -> a
  5. -> m b
#

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.

valueuniverse :: Plated a => a -> [a]
#

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

valueuniverseOf :: Getting (Endo [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]
valueuniverseOn :: Plated a => Getting (Endo [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.

valuecosmos :: Plated a => Fold a a
#

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

valuetransform :: 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
valuetransformM :: (Monad m, Plated a) => (a -> m a) -> a -> m a
#

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

valueholes :: 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 Control.Lens.Internal.Context.extract the original structure.

propChildren x = children l x == map pos (holes l x)
propId x = all (== x) [Control.Lens.Internal.Context.extract w | w <- holes l x]
holes = holesOf plate
valueholesOnOf
  1. :: Conjoined p
  2. => LensLike (Bazaar p r r) s t a b
  3. -> Over p (Bazaar p r r) a b r r
  4. -> s
  5. -> [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]
valuepara :: Plated a => (a -> [r] -> r) -> a -> r
#

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

para ≡ paraOf plate
valueparaOf :: 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

Compos

1 declaration

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

Parts

1 declaration
valueparts :: 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

4 declarations
valuegplate :: (Generic a, GPlated a (Rep a)) => Traversal' a a
#

Implement plate operation for a type using its Generic instance.

Note: the behavior may be different than with uniplate in some special cases. gplate doesn't look through other types in a group of mutually recursive types.

For example consider mutually recursive even and odd natural numbers:

Example1 expression
data Even = Z | E Odd deriving (Show, Generic, Data); data Odd = O Even deriving (Show, Generic, Data)

Then uniplate, which is based on Data, finds all even numbers less or equal than four:

Example2 expressions
import Data.Data.Lens (uniplate)universeOf uniplate (E (O (E (O Z))))[E (O (E (O Z))),E (O Z),Z]

but gplate doesn't see through Odd.

Example1 expression
universeOf gplate (E (O (E (O Z))))[E (O (E (O Z)))]

If using Data is not an option, you can still write the traversal manually. It is sometimes useful to use helper traversals

Example1 expression
:{let oddeven :: Traversal' Odd Even    oddeven f (O n) = O <$> f n    evenplate :: Traversal' Even Even    evenplate f Z     = pure Z    evenplate f (E n) = E <$> oddeven f n:}
Example1 expression
universeOf evenplate (E (O (E (O Z))))[E (O (E (O Z))),E (O Z),Z]
classclass GPlated a (g :: k -> Type) where
#
Instances8GPlated, …
  • GPlated a U1Defined in lens-5.3.5 · Control.Lens.Plated
  • GPlated a V1Defined in lens-5.3.5 · Control.Lens.Plated
  • GPlated a (URec b)Defined in lens-5.3.5 · Control.Lens.Plated
  • GPlated a (K1 i a)Defined in lens-5.3.5 · Control.Lens.Plated
  • GPlated a (K1 i b)Defined in lens-5.3.5 · Control.Lens.Plated
  • (GPlated a f, GPlated a g) => GPlated a (f :*: g)Defined in lens-5.3.5 · Control.Lens.Plated
  • (GPlated a f, GPlated a g) => GPlated a (f :+: g)Defined in lens-5.3.5 · Control.Lens.Plated
  • GPlated a f => GPlated a (M1 i c f)Defined in lens-5.3.5 · Control.Lens.Plated
classclass GPlated1 (f :: k -> Type) (g :: k -> Type) where
#
Instances11GPlated1, …