fixplate-0.1.6: Uniplate-style generic traversals for optionally annotated fixed-point types.

Safe HaskellSafe-Inferred
LanguageHaskell2010

Data.Generics.Fixplate

Description

This library provides Uniplate-style generic traversals and other recursion schemes for fixed-point types. There are many advantages of using fixed-point types instead of explicit recursion:

  • we can easily add attributes to the nodes of an existing tree;
  • there is no need for a custom type class, we can build everything on the top of Functor, Foldable and Traversable, for which GHC can derive the instances for us;
  • we can provide interesting recursion schemes
  • some operations can retain the structure of the tree, instead flattening it into a list;
  • it is relatively straightforward to provide generic implementations of the zipper, tries, tree drawing, hashing, etc...

The main disadvantage is that it does not work well for mutually recursive data types, and that pattern matching becomes more tedious (but there are partial solutions for the latter).

Consider as an example the following simple expression language, encoded by a recursive algebraic data type:

data Expr 
  = Kst Int 
  | Var String 
  | Add Expr Expr
  deriving (Eq,Show)

We can open up the recursion, and obtain a functor instead:

data Expr1 e 
  = Kst Int 
  | Var String 
  | Add e e 
  deriving (Eq,Show,Functor,Foldable,Traversable)

The fixed-point type Mu Expr1 is isomorphic to Expr. However, we can also add some attributes to the nodes: The type Attr Expr1 a = Mu (Ann Expr1 a) is the type of with the same structure, but with each node having an extra field of type a.

The functions in this library work on types like that: Mu f, where f is a functor, and sometimes explicitely on Attr f a.

The organization of the modules (excluding Util.*) is the following:

This module re-exports the most common functionality present in the library (but not for example the zipper, tries, hashing).

The library itself should be fully Haskell98 compatible; no language extensions are used. The only exception is the Data.Generics.Fixplate.Functor module, which uses the TypeOperators language extension for syntactic convenience (but this is not used anywhere else).

Note: to obtain Eq, Ord, Show, Read and other instances for fixed point types like Mu Expr1, consult the documentation of the EqF type class (cf. the related OrdF, ShowF and ReadF classes)

Synopsis

Documentation

class Functor f where

The Functor class is used for types that can be mapped over. Instances of Functor should satisfy the following laws:

fmap id  ==  id
fmap (f . g)  ==  fmap f . fmap g

The instances of Functor for lists, Maybe and IO satisfy these laws.

Methods

fmap :: (a -> b) -> f a -> f b

Instances

Functor [] 
Functor IO 
Functor Id 
Functor ZipList 
Functor STM 
Functor ReadPrec 
Functor ReadP 
Functor Maybe 
Functor ((->) r) 
Functor (Either a) 
Functor ((,) a) 
Functor (ST s) 
Ix i => Functor (Array i) 
Functor (StateL s) 
Functor (StateR s) 
Functor (Const m) 
Monad m => Functor (WrappedMonad m) 
Functor (ST s) 
Arrow a => Functor (ArrowMonad a) 
Functor (Proxy *) 
Functor (Map k) 
Functor f => Functor (CoAttrib f) 
Functor f => Functor (Attrib f) 
Arrow a => Functor (WrappedArrow a b) 
Functor f => Functor (CoAnn f a) 
Functor f => Functor (Ann f a) 
(Functor f, Functor g) => Functor ((:*:) f g) 
(Functor f, Functor g) => Functor ((:+:) f g) 
Functor f => Functor (HashAnn hash f) 

class Foldable t where

Data structures that can be folded.

Minimal complete definition: foldMap or foldr.

For example, given a data type

data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a)

a suitable instance would be

instance Foldable Tree where
   foldMap f Empty = mempty
   foldMap f (Leaf x) = f x
   foldMap f (Node l k r) = foldMap f l `mappend` f k `mappend` foldMap f r

This is suitable even for abstract types, as the monoid is assumed to satisfy the monoid laws. Alternatively, one could define foldr:

instance Foldable Tree where
   foldr f z Empty = z
   foldr f z (Leaf x) = f x z
   foldr f z (Node l k r) = foldr f (f k (foldr f z r)) l

Minimal complete definition

foldMap | foldr

Methods

fold :: Monoid m => t m -> m

Combine the elements of a structure using a monoid.

foldMap :: Monoid m => (a -> m) -> t a -> m

Map each element of the structure to a monoid, and combine the results.

foldr :: (a -> b -> b) -> b -> t a -> b

Right-associative fold of a structure.

foldr f z = foldr f z . toList

foldr' :: (a -> b -> b) -> b -> t a -> b

Right-associative fold of a structure, but with strict application of the operator.

foldl :: (b -> a -> b) -> b -> t a -> b

Left-associative fold of a structure.

foldl f z = foldl f z . toList

foldl' :: (b -> a -> b) -> b -> t a -> b

Left-associative fold of a structure. but with strict application of the operator.

foldl f z = foldl' f z . toList

foldr1 :: (a -> a -> a) -> t a -> a

A variant of foldr that has no base case, and thus may only be applied to non-empty structures.

foldr1 f = foldr1 f . toList

foldl1 :: (a -> a -> a) -> t a -> a

A variant of foldl that has no base case, and thus may only be applied to non-empty structures.

foldl1 f = foldl1 f . toList

Instances

class (Functor t, Foldable t) => Traversable t where

Functors representing data structures that can be traversed from left to right.

Minimal complete definition: traverse or sequenceA.

A definition of traverse must satisfy the following laws:

naturality
t . traverse f = traverse (t . f) for every applicative transformation t
identity
traverse Identity = Identity
composition
traverse (Compose . fmap g . f) = Compose . fmap (traverse g) . traverse f

A definition of sequenceA must satisfy the following laws:

naturality
t . sequenceA = sequenceA . fmap t for every applicative transformation t
identity
sequenceA . fmap Identity = Identity
composition
sequenceA . fmap Compose = Compose . fmap sequenceA . sequenceA

where an applicative transformation is a function

t :: (Applicative f, Applicative g) => f a -> g a

preserving the Applicative operations, i.e.

and the identity functor Identity and composition of functors Compose are defined as

  newtype Identity a = Identity a

  instance Functor Identity where
    fmap f (Identity x) = Identity (f x)

  instance Applicative Indentity where
    pure x = Identity x
    Identity f <*> Identity x = Identity (f x)

  newtype Compose f g a = Compose (f (g a))

  instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose x) = Compose (fmap (fmap f) x)

  instance (Applicative f, Applicative g) => Applicative (Compose f g) where
    pure x = Compose (pure (pure x))
    Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)

(The naturality law is implied by parametricity.)

Instances are similar to Functor, e.g. given a data type

data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a)

a suitable instance would be

instance Traversable Tree where
   traverse f Empty = pure Empty
   traverse f (Leaf x) = Leaf <$> f x
   traverse f (Node l k r) = Node <$> traverse f l <*> f k <*> traverse f r

This is suitable even for abstract types, as the laws for <*> imply a form of associativity.

The superclass instances should satisfy the following:

Minimal complete definition

traverse | sequenceA

Methods

traverse :: Applicative f => (a -> f b) -> t a -> f (t b)

Map each element of a structure to an action, evaluate these actions from left to right, and collect the results.

sequenceA :: Applicative f => t (f a) -> f (t a)

Evaluate each action in the structure from left to right, and collect the results.

mapM :: Monad m => (a -> m b) -> t a -> m (t b)

Map each element of a structure to a monadic action, evaluate these actions from left to right, and collect the results.

sequence :: Monad m => t (m a) -> m (t a)

Evaluate each monadic action in the structure from left to right, and collect the results.