Safe Haskell | None |
---|---|

Language | Haskell2010 |

Based on «Scrap Your Zippers: A Generic Zipper for Heterogeneous Types. Michael D. Adams. WGP '10: Proceedings of the 2010 ACM SIGPLAN workshop on Generic programming, 2010» (http://michaeldadams.org/papers/scrap_your_zippers/).

Compared to the original `syz`

package, this implementation (based on
`GTraversable`

) gives more flexibility as to where a zipper may point
to and what is considered as siblings.

Specifically, a zipper may point to any element which `gtraverse`

applies its function to.

## Example

### syz

Consider the classical example: lists. With syz, a list is interpreted as a right-balanced tree.

`>>>`

`let z = fromJust . down' $ toZipper ['a'..'d']`

`>>>`

Just 'a'`getHole z :: Maybe Char`

The zipper `z`

points to the first element of the list. Now let's
move to the right:

`>>>`

`let z' = fromJust . right $ z`

`>>>`

Nothing`getHole z' :: Maybe Char`

`>>>`

Just "bcd"`getHole z' :: Maybe [Char]`

Instead of pointing to the second element of the list, as one might
expect, the zipper `z'`

points to the tail of the list. In order to
actually move to the second element, we need another `down'`

:

`>>>`

`let z'' = fromJust . down' $ z'`

`>>>`

Just 'b'`getHole z'' :: Maybe Char`

### traverse-with-class

`GTraversable`

-based zippers behave more intuitively in this regard,
thanks to the uniform instance for lists.

`>>>`

`let z = fromJust . down' $ toZipper ['a'..'d'] :: Zipper Typeable [Char]`

`>>>`

Just 'a'`getHole z :: Maybe Char`

So far it's more or less the same as with syz. We needed to add a type
annotation for the zipper itself to clarify the context which should
be available at each hole (`Typeable`

in this case). Now let's see
what's to the right of us:

`>>>`

`let z' = fromJust . right $ z`

`>>>`

Just 'b'`getHole z' :: Maybe Char`

That is, we jumped right to the second element of the list. Likewise,

`>>>`

`let z'' = rightmost z`

`>>>`

Just 'd'`getHole z'' :: Maybe Char`

So, unlike in `syz`

, all of the list elements are siblings.

## Synopsis

- data Zipper (c :: * -> Constraint) root
- toZipper :: Rec c a => a -> Zipper c a
- fromZipper :: Zipper c a -> a
- left :: Zipper c a -> Maybe (Zipper c a)
- right :: Zipper c a -> Maybe (Zipper c a)
- down :: forall a c. Zipper c a -> Maybe (Zipper c a)
- down' :: Zipper c a -> Maybe (Zipper c a)
- up :: Zipper c a -> Maybe (Zipper c a)
- leftmost :: Zipper c a -> Zipper c a
- rightmost :: Zipper c a -> Zipper c a
- query :: (forall d. Rec c d => d -> b) -> Zipper c a -> b
- trans :: (forall d. Rec c d => d -> d) -> Zipper c a -> Zipper c a
- transM :: Monad m => (forall d. Rec c d => d -> m d) -> Zipper c a -> m (Zipper c a)
- getHole :: Typeable b => Zipper Typeable a -> Maybe b
- setHole :: Typeable a => a -> Zipper Typeable b -> Zipper Typeable b
- setHole' :: Typeable a => a -> Zipper Typeable b -> Maybe (Zipper Typeable b)

# Core types

data Zipper (c :: * -> Constraint) root Source #

A generic zipper with a root object of type `root`

.

# Core interface

## Injection and projection

toZipper :: Rec c a => a -> Zipper c a Source #

Create a zipper. The focus starts at the root of the object.

fromZipper :: Zipper c a -> a Source #

Move up a zipper to the root and return the root object.

## Basic movement

left :: Zipper c a -> Maybe (Zipper c a) Source #

Move left. Returns `Nothing`

iff already at leftmost sibling.

right :: Zipper c a -> Maybe (Zipper c a) Source #

Move right. Returns `Nothing`

iff already at rightmost sibling.

down :: forall a c. Zipper c a -> Maybe (Zipper c a) Source #

Move down. Moves to rightmost immediate child. Returns `Nothing`

iff at a leaf and thus no children exist.

down' :: Zipper c a -> Maybe (Zipper c a) Source #

Move down. Move to the leftmost immediate child. Returns `Nothing`

iff at a leaf and thus no children exist.

up :: Zipper c a -> Maybe (Zipper c a) Source #

Move up. Returns `Nothing`

iff already at root and thus no parent exists.

## Basic hole manipulation

query :: (forall d. Rec c d => d -> b) -> Zipper c a -> b Source #

Apply a generic query to the hole.

trans :: (forall d. Rec c d => d -> d) -> Zipper c a -> Zipper c a Source #

Apply a generic transformation to the hole.

transM :: Monad m => (forall d. Rec c d => d -> m d) -> Zipper c a -> m (Zipper c a) Source #

Apply a generic monadic transformation to the hole

# Convenience hole manipulation interface

It does not appear easy to make these functions polymorphic over the constraint `c`

.

If you want these functions for your own constraint (which entails
`Typeable`

), you need to copy their definitions and change `Typeable`

to your constraint in the `Zipper`

's argument.

getHole :: Typeable b => Zipper Typeable a -> Maybe b Source #

Get the value in the hole. Returns `Nothing`

iff `a`

is not the type of the value in the hole.