alloy-1.2.1: Generic programming library

Safe HaskellSafe-Inferred




A module of helper functions for use with Alloy. Most of the functions have versions for pure functions (without suffix), applicative functors (A suffix) and monads (M suffix) and sometimes the monadic version again with routes. Generally, only the pure version is documented. The key functions you are likely to need (or their suffixed versions) are applyBottomUp and applyBottomUp2, and listifyDepth.


Functions to easily apply transformations throughout a data structure

applyBottomUp :: (Alloy t (OneOp s) BaseOp, Alloy s BaseOp (OneOp s)) => (s -> s) -> t -> tSource

Given a function that applies to a particular type (s), automatically applies that function to every instance of s in a larger structure of type t, performing the transformations in a bottom-up fashion. It does a depth first traversal in order of a constructor's children, descending first and applying the function afterwards on the way back up.

This is equivalent to SYB's everywhere function, as it applies the function everywhere it can throughout the data structure. The function will not be applied to the results of your transformation, so the function cannot end up in infinite loop (unless the value you apply the function to is infinite!).

applyBottomUpA :: (AlloyA t (OneOpA s) BaseOpA, AlloyA s BaseOpA (OneOpA s), Applicative f) => f (s -> s) -> t -> f tSource

applyBottomUpM :: (AlloyA t (OneOpA s) BaseOpA, AlloyA s BaseOpA (OneOpA s), Monad m) => (s -> m s) -> t -> m tSource

applyBottomUp2 :: (Alloy t (TwoOp sA sB) BaseOp, Alloy sA BaseOp (TwoOp sA sB), Alloy sB BaseOp (TwoOp sA sB)) => (sA -> sA) -> (sB -> sB) -> t -> tSource

As applyBottomUp, but applies both functions whereever it can in the data structure. It is very important that sA is not the same type as sB -- odd results will occur if they are the same type. It is perfectly valid for sA to contain sB or vice versa; in this case, the smaller type will be processed first (as this is a bottom-up traversal) and the larger type processed later on in the ascent (towards the root) of the tree.

applyBottomUpA2 :: (AlloyA t (TwoOpA sA sB) BaseOpA, AlloyA sA BaseOpA (TwoOpA sA sB), AlloyA sB BaseOpA (TwoOpA sA sB), Applicative f) => f (sA -> sA) -> f (sB -> sB) -> t -> f tSource

applyBottomUpM2 :: (AlloyA t (TwoOpA sA sB) BaseOpA, AlloyA sA BaseOpA (TwoOpA sA sB), AlloyA sB BaseOpA (TwoOpA sA sB), Monad m) => (sA -> m sA) -> (sB -> m sB) -> t -> m tSource

applyBottomUpMRoute2 :: (AlloyARoute t (TwoOpARoute sA sB) BaseOpARoute, AlloyARoute sA BaseOpARoute (TwoOpARoute sA sB), AlloyARoute sB BaseOpARoute (TwoOpARoute sA sB), Monad m) => ((sA, Route sA t) -> m sA) -> ((sB, Route sB t) -> m sB) -> t -> m tSource

Listify functions that return lists of items that satisfy given criteria

listifyDepth :: (AlloyA t (OneOpA s) BaseOpA, AlloyA s BaseOpA (OneOpA s)) => (s -> Bool) -> t -> [s]Source

Given a function that examines a type s and gives an answer (True to include the item in the list, False to drop it), finds all items of type s in some larger item (of type t) that satisfy this function, listed in depth-first order.

Check functions to apply monadic checks throughout a data structure

checkDepthM :: (Monad m, AlloyA t (OneOpA s) BaseOpA, AlloyA s BaseOpA (OneOpA s)) => (s -> m ()) -> t -> m ()Source

Given a monadic function that operates on items of type s (without modifying them), applies the function to all items of types s within an item of type t, in depth-first order.

This can be used, for example, to perform checks on items in an error monad, or to accumulate information in a state monad, or to print out the structure in a writer or IO monad.

checkDepthM2 :: (Monad m, AlloyA t (TwoOpA r s) BaseOpA, AlloyA r BaseOpA (TwoOpA r s), AlloyA s BaseOpA (TwoOpA r s)) => (r -> m ()) -> (s -> m ()) -> t -> m ()Source

Adding traversal to modifiers

makeBottomUp :: Alloy t BaseOp opT => opT -> (t -> t) -> t -> tSource

Given a set of operations and a modifier function, augments that modifier function to first descend into the value before then applying the modifier function. This can be used to perform a bottom-up depth-first traversal of a structure (see the implementation of applyBottomUp).

You are unlikely to need these functions much yourself; either use applyBottomUp and similar to apply a function everywhere, or if you need more fine-grained control over the descent, it is usually better to handle the descent in your own functions.

makeBottomUpA :: (AlloyA t BaseOpA opT, Applicative f) => opT f -> f (t -> t) -> t -> f tSource

makeBottomUpM :: (AlloyA t BaseOpA opT, Monad m) => opT m -> (t -> m t) -> t -> m tSource

makeBottomUpMRoute :: (Monad m, AlloyARoute t BaseOpARoute opT) => opT m outer -> ((t, Route t outer) -> m t) -> (t, Route t outer) -> m tSource