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']
>>>
getHole z :: Maybe Char
Just 'a'
The zipper z
points to the first element of the list. Now let's
move to the right:
>>>
let z' = fromJust . right $ z
>>>
getHole z' :: Maybe Char
Nothing>>>
getHole z' :: Maybe [Char]
Just "bcd"
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'
>>>
getHole z'' :: Maybe Char
Just 'b'
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]
>>>
getHole z :: Maybe Char
Just 'a'
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
>>>
getHole z' :: Maybe Char
Just 'b'
That is, we jumped right to the second element of the list. Likewise,
>>>
let z'' = rightmost z
>>>
getHole z'' :: Maybe Char
Just 'd'
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.