Portability | non-portable |
---|---|

Stability | experimental |

Maintainer | Edward Kmett <ekmett@gmail.com> |

Safe Haskell | Trustworthy |

This module provides a `Zipper`

with fairly strong type checking guarantees.

The code here is inspired by Brandon Simmons' `zippo`

package, but uses
a slightly different approach to represent the `Zipper`

that makes the whole thing
look like his breadcrumb trail, and can move side-to-side through traversals.

Some examples types:

`Top`

`:>`

a- represents a trivial
`Zipper`

with its focus at the root. `Top`

`:>`

`Tree`

a`:>`

a- represents a zipper that starts with a
`Tree`

and descends in a single step to values of type`a`

. `Top`

`:>`

`Tree`

a`:>`

`Tree`

a`:>`

`Tree`

a- represents a
`Zipper`

into a`Tree`

with an intermediate bookmarked`Tree`

, focusing in yet another`Tree`

.

Since individual levels of a zipper are managed by an arbitrary `Traversal`

,
you can move left and right through the `Traversal`

selecting neighboring elements.

`>>>`

("Jelly","world")`zipper ("hello","world") % down _1 % fromWithin traverse % focus .~ 'J' % rightmost % focus .~ 'y' % rezip`

This is particularly powerful when compiled with `plate`

,
`uniplate`

or `biplate`

for walking down into
self-similar children in syntax trees and other structures.

- data Top
- data p :> a
- zipper :: a -> Top :> a
- focus :: SimpleIndexedLens (Tape (h :> a)) (h :> a) a
- up :: ((a :> b) :> c) -> a :> b
- down :: SimpleLensLike (Context c c) b c -> (a :> b) -> (a :> b) :> c
- within :: SimpleLensLike (Bazaar c c) b c -> (a :> b) -> Maybe ((a :> b) :> c)
- fromWithin :: SimpleLensLike (Bazaar c c) b c -> (a :> b) -> (a :> b) :> c
- left :: (a :> b) -> Maybe (a :> b)
- left1 :: (a :> b) -> a :> b
- lefts :: Int -> (h :> a) -> Maybe (h :> a)
- lefts1 :: Int -> (h :> a) -> h :> a
- leftmost :: (a :> b) -> a :> b
- right :: (a :> b) -> Maybe (a :> b)
- right1 :: (a :> b) -> a :> b
- rights :: Int -> (h :> a) -> Maybe (h :> a)
- rights1 :: Int -> (h :> a) -> h :> a
- rightmost :: (a :> b) -> a :> b
- goto :: Int -> (a :> b) -> Maybe (a :> b)
- goto1 :: Int -> (a :> b) -> a :> b
- coordinate :: (a :> b) -> Int
- width :: (a :> b) -> Int
- rezip :: Zipper h a => (h :> a) -> Zipped h a
- type family Zipped h a
- class Zipper h a
- data Tape k
- save :: (a :> b) -> Tape (a :> b)
- restore :: Tape (h :> a) -> Zipped h a -> Maybe (h :> a)
- restore1 :: Tape (h :> a) -> Zipped h a -> Maybe (h :> a)
- unsafelyRestore :: Tape (h :> a) -> Zipped h a -> h :> a

# Zippers

This is the type of a `Zipper`

. It visually resembes a 'breadcrumb trail' as
used in website navigation. Each breadcrumb in the trail represents a level you
can move up to.

This type operator associates to the right, so you can use a type like

`Top`

`:>`

(`String`

,`Double`

)`:>`

`String`

`:>`

`Char`

to represent a zipper from `(`

down to `String`

,`Double`

)`Char`

that has an intermediate
crumb for the `String`

containing the `Char`

.

## Focusing

## Horizontal movement

fromWithin :: SimpleLensLike (Bazaar c c) b c -> (a :> b) -> (a :> b) :> cSource

Unsafely step down into a `Traversal`

that is *assumed* to be non-empty.

If this invariant is not met then this will usually result in an error!

`fromWithin`

::`Simple`

`Traversal`

b c -> (a :> b) -> a :> b :> c`fromWithin`

::`Simple`

`Lens`

b c -> (a :> b) -> a :> b :> c`fromWithin`

::`Simple`

`Iso`

b c -> (a :> b) -> a :> b :> c

You can reason about this function as if the definition was:

`fromWithin`

l ≡ 'fromJust '`.`

`within`

l

but it is lazier in such a way that if this invariant is violated, some code can still succeed if it is lazy enough in the use of the focused value.

## Lateral movement

coordinate :: (a :> b) -> IntSource

Return the index into the current `Traversal`

.

`goto`

(`coordinate`

l) l = Just'

## Closing the Zipper

rezip :: Zipper h a => (h :> a) -> Zipped h aSource

Close something back up that you opened as a `zipper`

.

This represents the type a zipper will have when it is fully `Zipped`

back up.

## Saving your Progress

restore :: Tape (h :> a) -> Zipped h a -> Maybe (h :> a)Source

Restore ourselves to a previously recorded position precisely.

If the position does not exist, then fail.

restore1 :: Tape (h :> a) -> Zipped h a -> Maybe (h :> a)Source

Restore ourselves to a previously recorded position.

When moving left to right through a `Traversal`

, if this will clamp at each level to the range `0 <= k < width`

,
so the only failures will occur when one of the sequence of downward traversals find no targets.

unsafelyRestore :: Tape (h :> a) -> Zipped h a -> h :> aSource

Restore ourselves to a previously recorded position.

This assumes that nothing has been done in the meantime to affect the existence of anything on the entire path.

Motions left or right are clamped, but all traversals included on the `Tape`

are assumed to be non-empty.

Violate these assumptions at your own risk.