syb-0.7.1: src/Data/Generics/Schemes.hs
{-# LANGUAGE RankNTypes, ScopedTypeVariables, CPP #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.Generics.Schemes
-- Copyright : (c) The University of Glasgow, CWI 2001--2003
-- License : BSD-style (see the LICENSE file)
--
-- Maintainer : generics@haskell.org
-- Stability : experimental
-- Portability : non-portable (local universal quantification)
--
-- \"Scrap your boilerplate\" --- Generic programming in Haskell
-- See <http://www.cs.uu.nl/wiki/GenericProgramming/SYB>. The present module
-- provides frequently used generic traversal schemes.
--
-----------------------------------------------------------------------------
module Data.Generics.Schemes (
everywhere,
everywhere',
everywhereBut,
everywhereM,
somewhere,
everything,
everythingBut,
everythingWithContext,
listify,
something,
synthesize,
gsize,
glength,
gdepth,
gcount,
gnodecount,
gtypecount,
gfindtype
) where
------------------------------------------------------------------------------
#ifdef __HADDOCK__
import Prelude
#endif
import Data.Data
import Data.Generics.Aliases
import Control.Monad
-- | Apply a transformation everywhere in bottom-up manner
everywhere :: (forall a. Data a => a -> a)
-> (forall a. Data a => a -> a)
-- Use gmapT to recurse into immediate subterms;
-- recall: gmapT preserves the outermost constructor;
-- post-process recursively transformed result via f
--
everywhere f = go
where
go :: forall a. Data a => a -> a
go = f . gmapT go
-- | Apply a transformation everywhere in top-down manner
everywhere' :: (forall a. Data a => a -> a)
-> (forall a. Data a => a -> a)
-- Arguments of (.) are flipped compared to everywhere
everywhere' f = go
where
go :: forall a. Data a => a -> a
go = gmapT go . f
-- | Variation on everywhere with an extra stop condition
everywhereBut :: GenericQ Bool -> GenericT -> GenericT
-- Guarded to let traversal cease if predicate q holds for x
everywhereBut q f = go
where
go :: GenericT
go x
| q x = x
| otherwise = f (gmapT go x)
-- | Monadic variation on everywhere
everywhereM :: forall m. Monad m => GenericM m -> GenericM m
-- Bottom-up order is also reflected in order of do-actions
everywhereM f = go
where
go :: GenericM m
go x = do
x' <- gmapM go x
f x'
-- | Apply a monadic transformation at least somewhere
somewhere :: forall m. MonadPlus m => GenericM m -> GenericM m
-- We try "f" in top-down manner, but descent into "x" when we fail
-- at the root of the term. The transformation fails if "f" fails
-- everywhere, say succeeds nowhere.
--
somewhere f = go
where
go :: GenericM m
go x = f x `mplus` gmapMp go x
-- | Summarise all nodes in top-down, left-to-right order
everything :: forall r. (r -> r -> r) -> GenericQ r -> GenericQ r
-- Apply f to x to summarise top-level node;
-- use gmapQ to recurse into immediate subterms;
-- use ordinary foldl to reduce list of intermediate results
--
everything k f = go
where
go :: GenericQ r
go x = foldl k (f x) (gmapQ go x)
-- | Variation of "everything" with an added stop condition
everythingBut :: forall r. (r -> r -> r) -> GenericQ (r, Bool) -> GenericQ r
everythingBut k f = go
where
go :: GenericQ r
go x = let (v, stop) = f x
in if stop
then v
else foldl k v (gmapQ go x)
-- | Summarise all nodes in top-down, left-to-right order, carrying some state
-- down the tree during the computation, but not left-to-right to siblings.
everythingWithContext :: forall s r. s -> (r -> r -> r) -> GenericQ (s -> (r, s)) -> GenericQ r
everythingWithContext s0 f q = go s0
where
go :: s -> GenericQ r
go s x = foldl f r (gmapQ (go s') x)
where (r, s') = q x s
-- | Get a list of all entities that meet a predicate
listify :: Typeable r => (r -> Bool) -> GenericQ [r]
listify p = everything (++) ([] `mkQ` (\x -> if p x then [x] else []))
-- | Look up a subterm by means of a maybe-typed filter
something :: GenericQ (Maybe u) -> GenericQ (Maybe u)
-- "something" can be defined in terms of "everything"
-- when a suitable "choice" operator is used for reduction
--
something = everything orElse
-- | Bottom-up synthesis of a data structure;
-- 1st argument z is the initial element for the synthesis;
-- 2nd argument o is for reduction of results from subterms;
-- 3rd argument f updates the synthesised data according to the given term
--
synthesize :: forall s t. s -> (t -> s -> s) -> GenericQ (s -> t) -> GenericQ t
synthesize z o f = go
where
go :: GenericQ t
go x = f x (foldr o z (gmapQ go x))
-- | Compute size of an arbitrary data structure
gsize :: Data a => a -> Int
gsize t = 1 + sum (gmapQ gsize t)
-- | Count the number of immediate subterms of the given term
glength :: GenericQ Int
glength = length . gmapQ (const ())
-- | Determine depth of the given term
gdepth :: GenericQ Int
gdepth = (+) 1 . foldr max 0 . gmapQ gdepth
-- | Determine the number of all suitable nodes in a given term
gcount :: GenericQ Bool -> GenericQ Int
gcount p = everything (+) (\x -> if p x then 1 else 0)
-- | Determine the number of all nodes in a given term
gnodecount :: GenericQ Int
gnodecount = gcount (const True)
-- | Determine the number of nodes of a given type in a given term
gtypecount :: Typeable a => a -> GenericQ Int
gtypecount (_::a) = gcount (False `mkQ` (\(_::a) -> True))
-- | Find (unambiguously) an immediate subterm of a given type
gfindtype :: (Data x, Typeable y) => x -> Maybe y
gfindtype = singleton
. foldl unJust []
. gmapQ (Nothing `mkQ` Just)
where
unJust l (Just x) = x:l
unJust l Nothing = l
singleton [s] = Just s
singleton _ = Nothing