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 : List.NonEmpty.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 -> List.NonEmpty.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
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 -> Basics.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 -> Basics.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 -> Basics.Int
Get length of Zipper
custom [1,2] 3 [4]
|> length
--> 4
The following functions insert new values into an existing Zipper
.
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
These functions insert a 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
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 -> Basics.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 -> Basics.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 -> Basics.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
The following functions move the focus of a Zipper
without losing data.
These functions will return Nothing
when moving out of bounds of Zipper
.
next : Zipper a -> Maybe (Zipper a)
Move focus to next value.
custom [] 1 [2,3]
|> next
|> Maybe.map current
|> Just 2
custom [] 1 []
|> next
--> Nothing
prev : Zipper a -> Maybe (Zipper a)
Move focus to next value.
custom [1, 2] 3 []
|> prev
|> Maybe.map current
|> Just 2
custom [] 1 []
|> prev
--> Nothing
nextBy : Basics.Int -> Zipper a -> Maybe (Zipper a)
Perform next
n times.
prevBy : Basics.Int -> Zipper a -> Maybe (Zipper a)
Perform prev
n times.
These functions will move in one direction but won't end up out of bounds. When the end of one side is reached, the last value on that side is returned.
attemptNext : Zipper a -> Zipper a
Move focus to next value if such value exists.
custom [] 1 [2,3]
|> attemptNext
|> current
|> 2
custom [] 1 []
|> attemptNext
|> current
--> 1
attemptPrev : Zipper a -> Zipper a
Move focus to previous value if such value exists.
custom [1] 2 [3]
|> attemptPrev
|> current
|> 1
custom [] 1 []
|> attemptPrev
|> current
--> 1
attemptPrevBy : Basics.Int -> Zipper a -> Zipper a
Perform attemptPrev
n times.
attemptNextBy : Basics.Int -> Zipper a -> Zipper a
Perform attemptNext
n times.
These helper functions will move from either side of a Zipper
.
start : Zipper a -> Zipper a
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
These functions move in cycles around the zipper. The value at the start is preceded by the
value at the end. These functions simply move in circles and never reach the end of a Zipper
.
forward : Zipper a -> Zipper a
Move focus to next value, go back to first value if current value is last.
custom [] 1 [2,3]
|> forward
|> current
|> 2
custom [1,2] 3 []
|> forward
|> current
--> 1
backward : Zipper a -> Zipper a
Move focus to previous value, go to last value if current value is first one.
custom [1, 2] 3 []
|> backward
|> current
|> 2
custom [] 1 [2,3]
|> backward
|> current
--> 3
forwardBy : Basics.Int -> Zipper a -> Zipper a
Move forward
n times.
backwardBy : Basics.Int -> Zipper a -> Zipper a
Move backward
n times.
These functions let you shift the focus to the element which satisfies the predicate.
focusr : (a -> Basics.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 -> Basics.Bool) -> Zipper a -> Maybe (Zipper a)
Move focus to the first previous 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 -> Basics.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 : Basics.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
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 : Basics.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 : (Basics.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 : (Basics.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"
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]
duplicate : Zipper a -> Zipper (Zipper a)
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.
toNonEmpty : Zipper a -> List.NonEmpty.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]