This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.
README

List.Holey.Zipper

Like a regular old list-zipper, except it can also focus on the holes between elements.

This means you can represent an empty list, or point between two items and plug that hole with a value.

Types

type Zipper t a = Zipper (List a) (Maybe a) (List a)

Represents Zipper over a list with items of type a. The type t will, in practice, always be either Full or Hole. When it is Full, the zipper is focused on an item. When it is Hole, you're looking at a hole between elements.

type Full = Full

A Zipper Full a is pointing at an element of type a.

type Hole = Hole

A Zipper Hole a is pointing at a hole between as.

... Heh.

Creation

empty : Zipper Hole a

Create an empty zipper. It's pointing at nothing, there's nothing before it and nothing after it. It's the loneliest of all zippers.

import List.Holey.Zipper as Zipper


Zipper.toList Zipper.empty
--> []
singleton : a -> Zipper Full a

A zipper with a single thing in it. Singleton is just fancy-speak for single thing.

import List.Holey.Zipper as Zipper


Zipper.singleton "wat"
    |> Zipper.toList
--> [ "wat" ]
zipper : a -> List a -> Zipper Full a

Construct a zipper from the head of a list and some elements to come after it.

import List.Holey.Zipper as Zipper


Zipper.zipper "foo" []
--> Zipper.singleton "foo"


Zipper.zipper 0 [ 1, 2, 3 ]
    |> Zipper.toList
--> [ 0, 1, 2, 3 ]

Extraction

current : Zipper Full a -> a

Get the value the Zipper is currently pointing at.

Only applicable to zippers pointing at a value.

import List.Holey.Zipper as Zipper


Zipper.singleton "hi there"
    |> Zipper.current
--> "hi there"


Zipper.zipper 1 [ 2, 3, 4 ]
    |> Zipper.last
    |> Zipper.current
--> 4
before : Zipper t a -> List a

List the things that come before the current location in the zipper.

import List.Holey.Zipper as Zipper


Zipper.zipper 0 [ 1, 2, 3 ]
    |> Zipper.next
    |> Maybe.andThen Zipper.next
    |> Maybe.map Zipper.before
--> Just [ 0, 1 ]
after : Zipper t a -> List a

Conversely, list the things that come after the current location.

import List.Holey.Zipper as Zipper


Zipper.zipper 0 [ 1, 2, 3 ]
    |> Zipper.next
    |> Maybe.map Zipper.after
--> Just [ 2, 3 ]
toList : Zipper t a -> List a

Flattens the zipper (back) into a list.

import List.Holey.Zipper as Zipper


Zipper.toList Zipper.empty
--> []


Zipper.zipper 123 [ 789 ]
    |> Zipper.nextHole
    |> Zipper.plug 456
    |> Zipper.toList
--> [ 123, 456, 789 ]

Navigation

next : Zipper t a -> Maybe (Zipper Full a)

Move the zipper to the next item, if there is one.

import List.Holey.Zipper as Zipper


Zipper.zipper 0 [ 1, 2, 3 ]
    |> Zipper.next
    |> Maybe.map Zipper.current
--> Just 1

This also works from within holes:

Zipper.empty
    |> Zipper.insertAfter "foo"
    |> Zipper.next
--> Just <| Zipper.singleton "foo"

If there is no next thing, next is Nothing.

Zipper.empty
    |> Zipper.next
--> Nothing


Zipper.zipper 0 [ 1, 2, 3 ]
    |> Zipper.last
    |> Zipper.next
--> Nothing
previous : Zipper t a -> Maybe (Zipper Full a)

Move the zipper to the previous item, if there is one.

import List.Holey.Zipper as Zipper


Zipper.previous Zipper.empty
--> Nothing


Zipper.zipper "hello" [ "holey", "world" ]
    |> Zipper.last
    |> Zipper.previous
    |> Maybe.map Zipper.current
--> Just "holey"
nextHole : Zipper Full a -> Zipper Hole a

Move the zipper to the hole right after the current item. A hole is a whole lot of nothingness, so it's always there.

import List.Holey.Zipper as Zipper


Zipper.zipper "hello" [ "world" ]
    |> Zipper.nextHole
    |> Zipper.plug "holey"
    |> Zipper.toList
--> [ "hello", "holey", "world" ]
previousHole : Zipper Full a -> Zipper Hole a

Move the zipper to the hole right before the current item. Feel free to plug that hole right up!

import List.Holey.Zipper as Zipper


Zipper.singleton "world"
    |> Zipper.previousHole
    |> Zipper.plug "hello"
    |> Zipper.toList
--> [ "hello", "world" ]
first : Zipper Full a -> Zipper Full a

Go to the first element in the Zipper. Note that this only accepts a zipper that points at a thing, since it would have to return a Maybe otherwise.

If you want to zip back to the beginning of a zipper pointing at a hole, you can still use zipper |> previous |> Maybe.map first

import List.Holey.Zipper as Zipper


Zipper.zipper 1 [ 2, 3, 4 ]
    |> Zipper.prepend [ 4, 3, 2 ]
    |> Zipper.first
    |> Zipper.current
--> 4
last : Zipper Full a -> Zipper Full a

Go to the last element of a zipper. Same warnings as for first apply.

import List.Holey.Zipper as Zipper


Zipper.zipper 1 [ 2, 3, 4 ]
    |> Zipper.last
    |> Zipper.current
--> 4
beforeFirst : Zipper t a -> Zipper Hole a

Go to the hole before the first element. Remember that holes surround everything! They are everywhere.

import List.Holey.Zipper as Zipper


Zipper.zipper 1 [ 3, 4 ]
    -- now we're after 1
    |> Zipper.nextHole
    -- plug that hole
    |> Zipper.plug 2
    -- back to _before_ the first element
    |> Zipper.beforeFirst
    -- put something in that hole
    |> Zipper.plug 0
    -- and check the result
    |> Zipper.toList
--> [ 0, 1, 2, 3, 4 ]
afterLast : Zipper t a -> Zipper Hole a

Go to the hole after the end of the zipper. Into the nothingness.

findForward : (a -> Bool) -> Zipper t a -> Maybe (Zipper Full a)

Find the first element in the zipper the matches a predicate, returning a zipper pointing at that thing if it was found. When provided with a zipper pointing at a thing, that thing is also checked.

This start from the current location, and searches towards the end.

findBackward : (a -> Bool) -> Zipper t a -> Maybe (Zipper Full a)

Find the first element in the zipper matching a predicate, moving backwards from the current position.

Modification

map : (a -> b) -> Zipper t a -> Zipper t b

Execute a function on every item in the zipper.

import List.Holey.Zipper as Zipper


Zipper.zipper "first" [ "second", "third" ]
    |> Zipper.map String.toUpper
    |> Zipper.toList
--> [ "FIRST", "SECOND", "THIRD" ]
mapCurrent : (a -> a) -> Zipper t a -> Zipper t a

Execute a function on the current item in the zipper, when pointing at an item.

import List.Holey.Zipper as Zipper


Zipper.zipper "first" [ "second", "third" ]
    |> Zipper.mapCurrent String.toUpper
    |> Zipper.toList
--> [ "FIRST", "second", "third" ]
mapBefore : (a -> a) -> Zipper t a -> Zipper t a

Execute a function on all the things that came before the current location.

import List.Holey.Zipper as Zipper


Zipper.zipper "first" [ "second" ]
    |> Zipper.nextHole
    |> Zipper.mapBefore String.toUpper
    |> Zipper.toList
--> [ "FIRST", "second" ]
mapAfter : (a -> a) -> Zipper t a -> Zipper t a

Execute a function on all the things that come after the current location.

mapParts : { before : a -> b , current : a -> b , after : a -> b } -> Zipper t a -> Zipper t b

Execute a triplet of functions on the different parts of a zipper - what came before, what comes after, and the current thing if there is one.

import List.Holey.Zipper as Zipper


Zipper.zipper "first" [ "second" ]
    |> Zipper.nextHole
    |> Zipper.plug "one-and-a-halfth"
    |> Zipper.mapParts
        { before = (++) "before: "
        , current = (++) "current: "
        , after = (++) "after: "
        }
    |> Zipper.toList
--> [ "before: first"
--> , "current: one-and-a-halfth"
--> , "after: second"
--> ]
plug : a -> Zipper Hole a -> Zipper Full a

Plug a hole-y zipper.

import List.Holey.Zipper as Zipper


Zipper.plug "plug" Zipper.empty
--> Zipper.singleton "plug"
remove : Zipper Full a -> Zipper Hole a

Punch a hole into the zipper by removing an element entirely. You can think of this as collapsing the holes around the element into a single hole, but honestly the holes are everywhere.

import List.Holey.Zipper as Zipper


Zipper.zipper "hello" [ "holey", "world" ]
    |> Zipper.next
    |> Maybe.map Zipper.remove
    |> Maybe.map Zipper.toList
--> Just [ "hello", "world" ]
append : List a -> Zipper t a -> Zipper t a

Append a bunch of items after the zipper. This appends all the way at the end.

import List.Holey.Zipper as Zipper


Zipper.zipper 123 [ 456 ]
    |> Zipper.append [ 789, 0 ]
    |> Zipper.toList
--> [ 123, 456, 789, 0 ]
prepend : List a -> Zipper t a -> Zipper t a

Prepend a bunch of things to the zipper. All the way before anything else.

import List.Holey.Zipper as Zipper


Zipper.zipper 1 [ 2, 3, 4 ]
    |> Zipper.last
    |> Zipper.prepend [ 5, 6, 7 ]
    |> Zipper.toList
--> [ 5, 6, 7, 1, 2, 3, 4 ]
insertAfter : a -> Zipper t a -> Zipper t a

Insert something after the current location.

import List.Holey.Zipper as Zipper


Zipper.empty
    |> Zipper.insertAfter "hello"
    |> Zipper.toList
--> [ "hello" ]


Zipper.zipper 123 [ 789 ]
    |> Zipper.insertAfter 456
    |> Zipper.toList
--> [ 123, 456, 789 ]
insertBefore : a -> Zipper t a -> Zipper t a

Insert something before the current location.

import List.Holey.Zipper as Zipper


Zipper.empty
    |> Zipper.insertBefore "hello"
    |> Zipper.toList
--> [ "hello" ]


Zipper.singleton 123
    |> Zipper.insertBefore 456
    |> Zipper.toList
--> [ 456, 123 ]
module List.Holey.Zipper
    exposing
        ( Full
        , Hole
        , Zipper
        , after
        , afterLast
        , append
        , before
        , beforeFirst
        , current
        , empty
        , findBackward
        , findForward
        , first
        , insertAfter
        , insertBefore
        , last
        , map
        , mapAfter
        , mapBefore
        , mapCurrent
        , mapParts
        , next
        , nextHole
        , plug
        , prepend
        , previous
        , previousHole
        , remove
        , singleton
        , toList
        , zipper
        )

{-| Like a regular old list-zipper, except it can also focus on the holes
_between_ elements.

This means you can represent an empty list, or point between two items and plug
that hole with a value.


# Types

@docs Zipper, Full, Hole


# Creation

@docs empty, singleton, zipper


# Extraction

@docs current, before, after, toList


# Navigation

@docs next, previous, nextHole, previousHole, first, last, beforeFirst, afterLast, findForward, findBackward


# Modification

@docs map, mapCurrent, mapBefore, mapAfter, mapParts, plug, remove, append, prepend, insertAfter, insertBefore

-}


{-| Represents `Zipper` over a list with items of type `a`. The type `t` will,
in practice, always be either `Full` or `Hole`. When it is `Full`, the zipper is
focused on an item. When it is `Hole`, you're looking at a hole between elements.
-}
type Zipper t a
    = Zipper (List a) (Maybe a) (List a)


{-| A `Zipper Full a` is pointing at an element of type `a`.
-}
type Full
    = Full


{-| A `Zipper Hole a` is pointing at a hole between `a`s.

... Heh.

-}
type Hole
    = Hole


{-| Get the value the `Zipper` is currently pointing at.

Only applicable to zippers pointing at a value.

    import List.Holey.Zipper as Zipper


    Zipper.singleton "hi there"
        |> Zipper.current
    --> "hi there"


    Zipper.zipper 1 [ 2, 3, 4 ]
        |> Zipper.last
        |> Zipper.current
    --> 4

-}
current : Zipper Full a -> a
current (Zipper _ f _) =
    let
        {- Through the type-system, we ensure this never happens.
           It's a pinky promise.
        -}
        unsafe : Maybe a -> a
        unsafe m =
            case m of
                Just v ->
                    v

                Nothing ->
                    -- KABOOM
                    unsafe m
    in
    unsafe f


{-| Create an empty zipper. It's pointing at nothing, there's nothing before it
and nothing after it. It's the loneliest of all zippers.

    import List.Holey.Zipper as Zipper


    Zipper.toList Zipper.empty
    --> []

-}
empty : Zipper Hole a
empty =
    Zipper [] Nothing []


{-| A zipper with a single thing in it. Singleton is just fancy-speak for single
thing.

    import List.Holey.Zipper as Zipper


    Zipper.singleton "wat"
        |> Zipper.toList
    --> [ "wat" ]

-}
singleton : a -> Zipper Full a
singleton v =
    Zipper [] (Just v) []


{-| Construct a zipper from the head of a list and some elements to come after
it.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "foo" []
    --> Zipper.singleton "foo"


    Zipper.zipper 0 [ 1, 2, 3 ]
        |> Zipper.toList
    --> [ 0, 1, 2, 3 ]

-}
zipper : a -> List a -> Zipper Full a
zipper v after =
    Zipper [] (Just v) after


{-| List the things that come before the current location in the zipper.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 0 [ 1, 2, 3 ]
        |> Zipper.next
        |> Maybe.andThen Zipper.next
        |> Maybe.map Zipper.before
    --> Just [ 0, 1 ]

-}
before : Zipper t a -> List a
before (Zipper b _ _) =
    List.reverse b


{-| Conversely, list the things that come after the current location.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 0 [ 1, 2, 3 ]
        |> Zipper.next
        |> Maybe.map Zipper.after
    --> Just [ 2, 3 ]

-}
after : Zipper t a -> List a
after (Zipper _ _ a) =
    a


{-| Move the zipper to the next item, if there is one.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 0 [ 1, 2, 3 ]
        |> Zipper.next
        |> Maybe.map Zipper.current
    --> Just 1

This also works from within holes:

    Zipper.empty
        |> Zipper.insertAfter "foo"
        |> Zipper.next
    --> Just <| Zipper.singleton "foo"

If there is no `next` thing, `next` is `Nothing`.

    Zipper.empty
        |> Zipper.next
    --> Nothing


    Zipper.zipper 0 [ 1, 2, 3 ]
        |> Zipper.last
        |> Zipper.next
    --> Nothing

-}
next : Zipper t a -> Maybe (Zipper Full a)
next (Zipper b c a) =
    case a of
        [] ->
            Nothing

        n :: rest ->
            case c of
                Nothing ->
                    Just <| Zipper b (Just n) rest

                Just v ->
                    Just <| Zipper (v :: b) (Just n) rest


{-| Move the zipper to the previous item, if there is one.

    import List.Holey.Zipper as Zipper


    Zipper.previous Zipper.empty
    --> Nothing


    Zipper.zipper "hello" [ "holey", "world" ]
        |> Zipper.last
        |> Zipper.previous
        |> Maybe.map Zipper.current
    --> Just "holey"

-}
previous : Zipper t a -> Maybe (Zipper Full a)
previous (Zipper b c a) =
    case b of
        [] ->
            Nothing

        p :: rest ->
            case c of
                Nothing ->
                    Just <| Zipper rest (Just p) a

                Just v ->
                    Just <| Zipper rest (Just p) (v :: a)


{-| Move the zipper to the hole right after the current item. A hole is a whole
lot of nothingness, so it's always there.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "hello" [ "world" ]
        |> Zipper.nextHole
        |> Zipper.plug "holey"
        |> Zipper.toList
    --> [ "hello", "holey", "world" ]

-}
nextHole : Zipper Full a -> Zipper Hole a
nextHole ((Zipper b _ a) as z) =
    Zipper (current z :: b) Nothing a


{-| Move the zipper to the hole right before the current item. Feel free to plug
that hole right up!

    import List.Holey.Zipper as Zipper


    Zipper.singleton "world"
        |> Zipper.previousHole
        |> Zipper.plug "hello"
        |> Zipper.toList
    --> [ "hello", "world" ]

-}
previousHole : Zipper Full a -> Zipper Hole a
previousHole ((Zipper b _ a) as z) =
    Zipper b Nothing (current z :: a)


{-| Plug a hole-y zipper.

    import List.Holey.Zipper as Zipper


    Zipper.plug "plug" Zipper.empty
    --> Zipper.singleton "plug"

-}
plug : a -> Zipper Hole a -> Zipper Full a
plug v (Zipper b _ a) =
    Zipper b (Just v) a


{-| Punch a hole into the zipper by removing an element entirely. You can think
of this as collapsing the holes around the element into a single hole, but
honestly the holes are everywhere.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "hello" [ "holey", "world" ]
        |> Zipper.next
        |> Maybe.map Zipper.remove
        |> Maybe.map Zipper.toList
    --> Just [ "hello", "world" ]

-}
remove : Zipper Full a -> Zipper Hole a
remove (Zipper b _ a) =
    Zipper b Nothing a


{-| Insert something after the current location.

    import List.Holey.Zipper as Zipper


    Zipper.empty
        |> Zipper.insertAfter "hello"
        |> Zipper.toList
    --> [ "hello" ]


    Zipper.zipper 123 [ 789 ]
        |> Zipper.insertAfter 456
        |> Zipper.toList
    --> [ 123, 456, 789 ]

-}
insertAfter : a -> Zipper t a -> Zipper t a
insertAfter v (Zipper b c a) =
    Zipper b c (v :: a)


{-| Insert something before the current location.

    import List.Holey.Zipper as Zipper


    Zipper.empty
        |> Zipper.insertBefore "hello"
        |> Zipper.toList
    --> [ "hello" ]


    Zipper.singleton 123
        |> Zipper.insertBefore 456
        |> Zipper.toList
    --> [ 456, 123 ]

-}
insertBefore : a -> Zipper t a -> Zipper t a
insertBefore v (Zipper b c a) =
    Zipper (v :: b) c a


{-| Flattens the zipper (back) into a list.

    import List.Holey.Zipper as Zipper


    Zipper.toList Zipper.empty
    --> []


    Zipper.zipper 123 [ 789 ]
        |> Zipper.nextHole
        |> Zipper.plug 456
        |> Zipper.toList
    --> [ 123, 456, 789 ]

-}
toList : Zipper t a -> List a
toList (Zipper b c a) =
    case c of
        Nothing ->
            List.reverse b ++ a

        Just v ->
            List.reverse b ++ v :: a


{-| Append a bunch of items after the zipper. This appends all the way at the end.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 123 [ 456 ]
        |> Zipper.append [ 789, 0 ]
        |> Zipper.toList
    --> [ 123, 456, 789, 0 ]

-}
append : List a -> Zipper t a -> Zipper t a
append xs (Zipper b c a) =
    Zipper b c (a ++ xs)


{-| Prepend a bunch of things to the zipper. All the way before anything else.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 1 [ 2, 3, 4 ]
        |> Zipper.last
        |> Zipper.prepend [ 5, 6, 7 ]
        |> Zipper.toList
    --> [ 5, 6, 7, 1, 2, 3, 4 ]

-}
prepend : List a -> Zipper t a -> Zipper t a
prepend xs (Zipper b c a) =
    Zipper (b ++ List.reverse xs) c a


{-| Go to the first element in the Zipper. Note that this only accepts a zipper
that points at a thing, since it would have to return a `Maybe` otherwise.

If you want to zip back to the beginning of a zipper pointing at a hole, you can
still use `zipper |> previous |> Maybe.map first`

    import List.Holey.Zipper as Zipper


    Zipper.zipper 1 [ 2, 3, 4 ]
        |> Zipper.prepend [ 4, 3, 2 ]
        |> Zipper.first
        |> Zipper.current
    --> 4

-}
first : Zipper Full a -> Zipper Full a
first ((Zipper b c a) as zipper) =
    case List.reverse b of
        [] ->
            zipper

        x :: xs ->
            Zipper [] (Just x) (xs ++ current zipper :: a)


{-| Go to the last element of a zipper. Same warnings as for `first` apply.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 1 [ 2, 3, 4 ]
        |> Zipper.last
        |> Zipper.current
    --> 4

-}
last : Zipper Full a -> Zipper Full a
last ((Zipper b c a) as zipper) =
    case List.reverse a of
        [] ->
            zipper

        x :: xs ->
            Zipper (xs ++ current zipper :: b) (Just x) []


{-| Go to the hole before the first element. Remember that holes surround
everything! They are everywhere.

    import List.Holey.Zipper as Zipper


    Zipper.zipper 1 [ 3, 4 ]
        -- now we're after 1
        |> Zipper.nextHole
        -- plug that hole
        |> Zipper.plug 2
        -- back to _before_ the first element
        |> Zipper.beforeFirst
        -- put something in that hole
        |> Zipper.plug 0
        -- and check the result
        |> Zipper.toList
    --> [ 0, 1, 2, 3, 4 ]

-}
beforeFirst : Zipper t a -> Zipper Hole a
beforeFirst ((Zipper b c a) as zipper) =
    case c of
        Nothing ->
            Zipper [] Nothing (List.reverse b ++ a)

        Just v ->
            Zipper [] Nothing (List.reverse b ++ v :: a)


{-| Go to the hole after the end of the zipper. Into the nothingness.
-}
afterLast : Zipper t a -> Zipper Hole a
afterLast ((Zipper b c a) as zipper) =
    case c of
        Nothing ->
            Zipper (List.reverse a ++ b) Nothing []

        Just v ->
            Zipper (List.reverse a ++ v :: b) Nothing []


{-| Find the first element in the zipper the matches a predicate, returning a
zipper pointing at that thing if it was found. When provided with a zipper
pointing at a thing, that thing is also checked.

This start from the current location, and searches towards the end.

-}
findForward : (a -> Bool) -> Zipper t a -> Maybe (Zipper Full a)
findForward predicate ((Zipper b c a) as zipper) =
    if Maybe.withDefault False (Maybe.map predicate c) then
        Just <| Zipper b c a
    else
        next zipper |> Maybe.andThen (findForward predicate)


{-| Find the first element in the zipper matching a predicate, moving backwards
from the current position.
-}
findBackward : (a -> Bool) -> Zipper t a -> Maybe (Zipper Full a)
findBackward predicate ((Zipper b c a) as zipper) =
    if Maybe.withDefault False (Maybe.map predicate c) then
        Just <| Zipper b c a
    else
        previous zipper |> Maybe.andThen (findBackward predicate)


{-| Execute a function on every item in the zipper.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "first" [ "second", "third" ]
        |> Zipper.map String.toUpper
        |> Zipper.toList
    --> [ "FIRST", "SECOND", "THIRD" ]

-}
map : (a -> b) -> Zipper t a -> Zipper t b
map f (Zipper b c a) =
    Zipper (List.map f b) (Maybe.map f c) (List.map f a)


{-| Execute a function on the current item in the zipper, when pointing at an
item.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "first" [ "second", "third" ]
        |> Zipper.mapCurrent String.toUpper
        |> Zipper.toList
    --> [ "FIRST", "second", "third" ]

-}
mapCurrent : (a -> a) -> Zipper t a -> Zipper t a
mapCurrent f (Zipper b c a) =
    Zipper b (Maybe.map f c) a


{-| Execute a function on all the things that came before the current location.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "first" [ "second" ]
        |> Zipper.nextHole
        |> Zipper.mapBefore String.toUpper
        |> Zipper.toList
    --> [ "FIRST", "second" ]

-}
mapBefore : (a -> a) -> Zipper t a -> Zipper t a
mapBefore f (Zipper b c a) =
    Zipper (List.map f b) c a


{-| Execute a function on all the things that come after the current location.
-}
mapAfter : (a -> a) -> Zipper t a -> Zipper t a
mapAfter f (Zipper b c a) =
    Zipper b c (List.map f a)


{-| Execute a triplet of functions on the different parts of a zipper - what
came before, what comes after, and the current thing if there is one.

    import List.Holey.Zipper as Zipper


    Zipper.zipper "first" [ "second" ]
        |> Zipper.nextHole
        |> Zipper.plug "one-and-a-halfth"
        |> Zipper.mapParts
            { before = (++) "before: "
            , current = (++) "current: "
            , after = (++) "after: "
            }
        |> Zipper.toList
    --> [ "before: first"
    --> , "current: one-and-a-halfth"
    --> , "after: second"
    --> ]

-}
mapParts :
    { before : a -> b
    , current : a -> b
    , after : a -> b
    }
    -> Zipper t a
    -> Zipper t b
mapParts conf (Zipper b c a) =
    Zipper (List.map conf.before b) (Maybe.map conf.current c) (List.map conf.after a)