turboMaCk/non-empty-list-alias/1.3.0

NOTE: the latest version is 1.4.0

List.NonEmpty.Zipper

type Zipper a

Zipper type.

This can be thought of as NonEmpty which holds keeps track of unconsed data.

Unlike NonEmpty this type is opaque as it needs to ensure internal invariants.

singleton : a -> Zipper a

Put single value into a Zipper.

singleton "foo"
|> current
--> "foo"
fromNonEmpty : NonEmpty a -> Zipper a

Init Zipper from NonEmpty list type.

fromNonEmpty ( 1, [ 2, 3 ] )
|> current
--> 1


fromNonEmpty ( 1, [ 2, 3 ] )
|> toList
--> [ 1, 2, 3 ]
fromList : List a -> Maybe (Zipper a)

Init Zipper from List. This operation is not successful for []

fromList []
--> Nothing


fromList [1, 2, 3]
--> Just (custom [] 1 [2,3])
fromCons : a -> List a -> Zipper a

Init Zipper by consing value onto the list.

fromCons 1 [ 2, 3 ]
|> current
--> 1
fromConsList : List a -> NonEmpty a -> Zipper a

Init Zipper by consing List onto NonEmpty.

The head of NonEmpty stays in focus while list is a list of previous heads.

fromConsList [] (1, [2])
|> current
--> 1


fromConsList [1, 2] (3, [4])
|> prev
|> Maybe.map current
--> Just 2
custom : List a -> a -> List a -> Zipper a

Init Zipper from parts.

custom [1,2] 3 [4,5]
|> current
--> 3


custom [1,2] 3 [4,5]
|> prev
|> Maybe.map current
--> Just 2

Query

Functions that query Zipper for additional data.

current : Zipper a -> a

Get current focus

custom [1,2] 3 [4,5]
|> current
--> 3
listPrev : Zipper a -> List a

Get List of all values preceding current focus.

custom [1,2] 3 [4,5]
|> listPrev
--> [1,2]
listNext : Zipper a -> List a

Get List of all values following current focus.

custom [1,2] 3 [4,5]
|> listNext
--> [4,5]
hasPrev : Zipper a -> Bool

Check if there is next value before current focus.

custom [1,2] 3 [4,5]
|> hasPrev
--> True


custom [] 1 [2,3]
|> hasPrev
--> False
hasNext : Zipper a -> Bool

Check if there is next value after current focus.

custom [1,2] 3 [4,5]
|> hasNext
--> True


custom [1,2] 3 []
|> hasNext
--> False
length : Zipper a -> Int

Get length of Zipper

custom [1,2] 3 [4]
|> length
--> 4

Insert new values

Following function inserts new value into existing Zipper.

Insert without changing focus

These functions insert values without moving focus.

insertBefore : a -> Zipper a -> Zipper a

Insert new value before current focus.

fromConsList [1, 2] (4, [5])
|> insertBefore 3
|> toList
--> [1,2,3,4,5]


fromConsList [1, 2] (4, [5])
|> insertBefore 3
|> current
--> 4
insertAfter : a -> Zipper a -> Zipper a

Insert new value after current focus.

fromConsList [1, 2] (3, [5])
|> insertAfter 4
|> toList
--> [1,2,3,4,5]


fromConsList [1, 2] (3, [5])
|> insertAfter 4
|> current
--> 3
prepend : List a -> Zipper a -> Zipper a

Prepend Zipper with a List of values

Note: This has a linear complexity, meaning that if the number of items before the current focus doubles, the time this function takes also doubles.

fromConsList [3] (4, [5])
|> prepend [1, 2]
|> toList
--> [1,2,3,4,5]


fromConsList [2, 3] (4, [5])
|> prepend [1]
|> current
--> 4
append : List a -> Zipper a -> Zipper a

Append Zipper with a List of values

Note: This has a linear complexity, meaning that if the number of items after the current focus doubles, the time this function takes also doubles.

fromConsList [1] (2, [3])
|> append [4, 5]
|> toList
--> [1,2,3,4,5]


fromConsList [1] (2, [3])
|> append [4, 5]
|> current
--> 2

Insert and change focus

These functions insert value around focus while moving focus on newly inserted value.

consBefore : a -> Zipper a -> Zipper a

Insert value before current focus and move focus to it.

fromConsList [1, 2] (4, [5])
|> consBefore 3
|> toList
--> [1,2,3,4,5]


fromConsList [1, 2] (4, [5])
|> consBefore 3
|> current
--> 3
consAfter : a -> Zipper a -> Zipper a

Insert new value after current focus and move focus to it.

fromConsList [1, 2] (3, [5])
|> consAfter 4
|> toList
--> [1,2,3,4,5]


fromConsList [1, 2] (3, [5])
|> consAfter 4
|> current
--> 4

Remove elements from Zipper

dropr : Zipper a -> Maybe (Zipper a)

Drop currently focused item. This function shift focus to next element if such element exists or focuses the first one. In case of singleton Zipper this results to Nothing.

fromConsList [1, 2] (3, [4])
|> dropr
|> Maybe.map toList
--> Just [1, 2, 4]


fromConsList [1, 2] (3, [4])
|> dropr
|> Maybe.map current
--> Just 4


fromConsList [1, 2] (3, [])
|> dropr
|> Maybe.map current
--> Just 1


singleton 1
|> dropr
--> Nothing
dropl : Zipper a -> Maybe (Zipper a)

Drop currently focused item. This function shift focus to previous element if such element exists or focuses the last one. In case of singleton Zipper this results to Nothing.

fromConsList [1, 2] (3, [4])
|> dropl
|> Maybe.map toList
--> Just [1, 2, 4]


fromConsList [1, 2] (3, [4])
|> dropl
|> Maybe.map current
--> Just 2


fromConsList [] (1, [2, 3])
|> dropl
|> Maybe.map current
--> Just 3


singleton 1
|> dropl
--> Nothing
filterr : (a -> Bool) -> Zipper a -> Maybe (Zipper a)

Filter Zipper while moving focus to next element in case the current focus doesn't satisfy the predicate. All elements (including previous) are filtered by given predicate.

fromConsList [1,2] (3, [4])
|> filterr (\x -> modBy 2 x == 0)
|> Maybe.map toList
--> Just [2,4]


fromConsList [1,2] (3, [4])
|> filterr (\x -> modBy 2 x == 0)
|> Maybe.map current
--> Just 4


fromConsList [1,2] (3, [])
|> filterr (\x -> modBy 2 x == 0)
--> Nothing
filterl : (a -> Bool) -> Zipper a -> Maybe (Zipper a)

Filter Zipper while moving focus to previous element in case the current focus doesn't satisfy the predicate. All elements (including previous) are filtered by given predicate.

fromConsList [1,2] (3, [4])
|> filterl (\x -> modBy 2 x == 0)
|> Maybe.map toList
--> Just [2,4]


fromConsList [1,2] (3, [4])
|> filterl (\x -> modBy 2 x == 0)
|> Maybe.map current
--> Just 2


fromConsList [1] (3, [4])
|> filterl (\x -> modBy 2 x == 0)
--> Nothing
filter : (a -> Bool) -> Zipper a -> Maybe (Zipper a)

Filter Zipper while moving focus to next element in case the current focus doesn't satisfy the predicate. If even that fails it tries to focus previes element which satisfies the predicate. If predicate fails for all the elements, Nothing is returned.

fromConsList [1,2] (3, [4])
|> filter (\x -> modBy 2 x == 0)
|> Maybe.map toList
--> Just [2,4]


fromConsList [1,2] (3, [4])
|> filter (\x -> modBy 2 x == 0)
|> Maybe.map current
--> Just 4


fromConsList [1,2] (3, [])
|> filter (\x -> modBy 2 x == 0)
|> Maybe.map current
--> Just 2


fromConsList [1,2] (3, [])
|> filter ((==) 10)
--> Nothing

Movement

Functions that move focus within Zipper around without losing data.

Bounded Movement

These function will return Nothing when moving out of bounds of Zipper.

nextBy : Int -> Zipper a -> Maybe (Zipper a)

Perform next n times.

prevBy : Int -> Zipper a -> Maybe (Zipper a)

Perform prev n times.

Direction Movement

These function will move in direction but won't reach out of bound. When end on any side is reached, the last value on this side is returned.

Move focus to next value if such value exists.

custom [] 1 [2,3]
|> attemptNext
|> current
|> 2


custom [] 1 []
|> attemptNext
|> current
--> 1

Move focus to previous value if such value exists.

custom [1] 2 [3]
|> attemptPrev
|> current
|> 1


custom [] 1 []
|> attemptPrev
|> current
--> 1
attemptPrevBy : Int -> Zipper a -> Zipper a

Perform attemptPrev n times.

attemptNextBy : Int -> Zipper a -> Zipper a

Perform attemptNext n times.

Bounds

These helper function will move from either side of a Zipper

Move focus to the very first value

custom [ 1, 2, 3 ] 4 [ 5, 6, 7 ]
|> start
|> current
--> 1
end : Zipper a -> Zipper a

Move focus to the very last value

custom [ 1, 2, 3 ] 4 [ 5, 6, 7 ]
|> end
|> current
--> 7

Cycling Movement

These function move in cycles around the zipper. Value on a very start is preceded by value in the end. These function simply move in circle and never reach the end of a Zipper.

Move focus to next value, go back to fist value if current value is last.

custom [] 1 [2,3]
|> forward
|> current
|> 2


custom [1,2] 3 []
|> forward
|> current
--> 1

Move focus to previous value, go to last value if current value is fist one.

custom [1, 2] 3 []
|> backward
|> current
|> 2


custom [] 1 [2,3]
|> backward
|> current
--> 3
forwardBy : Int -> Zipper a -> Zipper a

Move forward n times.

backwardBy : Int -> Zipper a -> Zipper a

Move backward n times.

Free Movement

These fucntions let you shift the focus to the element which satisfy the predicate

focusr : (a -> Bool) -> Zipper a -> Maybe (Zipper a)

Move focus to the first next element that satisfy the predicate.

fromConsList [1,2] (3, [4, 5])
|> focusr ((==) 5)
|> Maybe.map current
--> Just 5


fromConsList [1,2] (3, [4, 5])
|> focusr ((==) 3)
|> Maybe.map current
--> Just 3


fromConsList [1,2] (3, [4, 5])
|> focusr ((==) 1)
|> Maybe.map current
--> Nothing
focusl : (a -> Bool) -> Zipper a -> Maybe (Zipper a)

Move focus to the first next element that satisfy the predicate

fromConsList [1,2] (3, [4, 5])
|> focusl ((==) 1)
|> Maybe.map current
--> Just 1


fromConsList [1,2] (3, [4, 5])
|> focusl ((==) 3)
|> Maybe.map current
--> Just 3


fromConsList [1,2] (3, [4, 5])
|> focusl ((==) 4)
|> Maybe.map current
--> Nothing
focus : (a -> Bool) -> Zipper a -> Maybe (Zipper a)

Focus next element by predicate. If no element satisfy predicate, try to select previous element. If even previous element doesn't satisfy predicate, return nothing.

fromConsList [1,2] (3, [4])
|> focus (\x -> modBy 2 x == 0)
|> Maybe.map current
--> Just 4


fromConsList [1,2] (3, [])
|> focus (\x -> modBy 2 x == 0)
|> Maybe.map current
--> Just 2


fromConsList [1,2] (3, [4])
|> focus ((==) 5)
--> Nothing
goToIndex : Int -> Zipper a -> Maybe (Zipper a)

Moves zipper to the given index.

This function can be potentially O(n) operation if at the last item and trying to go to last index.

custom ['A', 'B'] 'C' ['D', 'E', 'F']
  |> goToIndex 3
  --> Just <| custom ['A', 'B', 'C'] 'D' ['E', 'F']


custom ['A', 'B'] 'C' ['D', 'E', 'F']
  |> goToIndex 6
  --> Nothing

Transform

update : (a -> a) -> Zipper a -> Zipper a

Update curently focused item by given function

fromConsList [1,2] (2, [3, 4])
|> update (\x -> x * x)
|> toList
--> [1, 2, 4, 3, 4]
map : (a -> b) -> Zipper a -> Zipper b

Map a function over Zipper

map String.fromInt (custom [1] 2 [3, 4])
|> toList
--> ["1", "2", "3", "4"]
updateAtIndex : Int -> (a -> a) -> Zipper a -> Maybe (Zipper a)

Map only the element in the zipper at the given index.

custom ['A', 'B'] 'C' ['D', 'E', 'F']
  |> updateAtIndex 1 Char.toLower
  --> Just <| custom ['A', 'b'] 'C' ['D', 'E', 'F']
relativeIndexedMap : (Int -> a -> b) -> Zipper a -> Zipper b

Indexed map relative to the position in the zipper.

custom ["a", "b"] "c" ["d"]
|> relativeIndexedMap (\index el -> (index, el))
|> toList
--> [(-2,"a"),(-1,"b"), (0,"c"), (1,"d")]
absoluteIndexedMap : (Int -> a -> b) -> Zipper a -> Zipper b

Indexed map. Starting with 0 from the beginning of the zipper

custom ["a", "b"] "c" ["d"]
|> absoluteIndexedMap (\index el -> (index, el))
|> toList
--> [(0,"a"),(1,"b"), (2,"c"), (3,"d")]
foldl : (a -> b -> b) -> b -> Zipper a -> b

Reduce Zipper from left

foldl (+) 0 <| custom [1,2] 3 [4]
--> 10
foldr : (a -> b -> b) -> b -> Zipper a -> b

Reduce Zipper from right

foldr (+) 0 <| custom [1,2] 3 [4]
--> 10
foldl1 : (a -> a -> a) -> Zipper a -> a

Collapse Zipper a into a value from left

foldl1 (++) <| custom ["hello"] " " ["world"]
--> "world hello"
foldr1 : (a -> a -> a) -> Zipper a -> a

Collapse Zipper a into a value from right

foldr1 (+) (custom [1,2] 3 [4])
--> 10


foldr1 (++) (custom ["hello"] " " ["world"])
--> "hello world"

Combine

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

Combine two Zippers with a given function. In case where one of the two zippers is longer the extra elements are ignored

map2 (+) (custom [1] 2 []) (custom [1] 1 [])
|> toList
--> [2, 3]


map2 (+) (custom [1] 2 [3]) (custom [1] 1 [])
|> toList
--> [2, 3]
andMap : Zipper a -> Zipper (a -> b) -> Zipper b

Map over multiple Zippers.

map (+) (custom [1] 2 [3])
|> andMap (custom [1] 2 [3])
|> toList
--> [2, 4, 6]

Expand

Create Zipper containing all possible variants of given Zipper. Current version is focused one.

custom [1] 2 [3]
|> duplicate
|> current
--> custom [1] 2 [3]




custom [1] 2 [3]
|> duplicate
|> forward
|> current
--> custom [1, 2] 3 []
extend : (Zipper a -> b) -> Zipper a -> Zipper b

Map value to a new value based on surrounding structure.

This is a more advanced function following Comonad

-- negate all True values which next value is not True itself
fromNonEmpty ( True, [ True, True, False, True, True ] )
|> extend (\zipper ->
                  let prev = current <| backward zipper
                  in prev && current zipper
             )
|> toNonEmpty
--> (True, [True, True, False, False, True])
duplicateList : Zipper a -> List (Zipper a)

duplicate and covert to List.

This function might be useful in view code.

Convert

toNonEmpty : Zipper a -> NonEmpty a

Convert Zipper back to NonEmpty.

This function won't loose data, all previous heads are added back.

fromCons 1 [2,3]
|> toNonEmpty
--> (1, [2, 3])




fromConsList [1,2] (3, [4])
|> toNonEmpty
--> (1, [2,3,4])
toList : Zipper a -> List a

Convert Zipper to List.

singleton 1
|> toList
--> [1]


custom [1,2] 3 []
|> toList
--> [1,2,3]