A pivot is a list upgraded with a center and sides. However, a pivot can never be empty, so it is better to think of it an upgraded cons list.
In this library, we suffix functions with letters to denote their context, as follows.
This way you can guess a function's name easily.
See the different map*
functions.
For example, getL
gets the left side of a pivot.
Types.Pivot a
Pivot is an opaque data type. A Pivot represents a list with a pointer to the center.
A pivot of the list [1, 2, 3, 4]
with 3 as the center shall be represented by [1 2 *3* 4]
.
So you want to use a pivot? Better know how to create one, and get stuff back!
fromList : List a -> Maybe (Pivot a)
Make a pivot from a list with empty left side.
Fails if and only if the list given is empty.
fromList [] == Nothing
fromList [1, 2, 3, 4] == Just [*1* 2 3 4]
fromCons : a -> List a -> Pivot a
Like fromList
, but by specifying the center explicitly, it cannot fail.
fromCons 1 [2, 3, 4] == [*1* 2 3 4]
Just (fromCons h t) == fromList (h :: t)
singleton : a -> Pivot a
Like fromCons
, but without the list. That is, we specify only the center.
singleton 3 == [*3*]
singleton x == fromCons x []
getC : Pivot a -> a
Get the center member.
getC [ 1 2 * 3 * 4 ] == 3
singleton >> getC == identity
getL : Pivot a -> List a
Get the left side list.
getL [ 1 2 * 3 * 4 ] == [ 1, 2 ]
getR : Pivot a -> List a
Get the right side list.
getR [ 1 2 * 3 * 4 ] == [ 4 ]
getA : Pivot a -> List a
Make the pivot into a list.
getA [ 1 2 * 3 * 4 ] == [ 1, 2, 3, 4 ]
toList : Pivot a -> List a
Alias for getA
hasL : Pivot a -> Basics.Bool
Check if the left side is not empty.
hasR : Pivot a -> Basics.Bool
Check if the right side is not empty.
Handle the position of the center.
These functions do not mutate the underlying list.
That is, if you apply functions from here and then apply getA
,
you'd get the same thing you would by applying getA
beforehand.
lengthL : Pivot a -> Basics.Int
Position from the left side. Starts with 0.
lengthR : Pivot a -> Basics.Int
Position from the right side. Starts with 0.
lengthA : Pivot a -> Basics.Int
Length of the pivot.
lengthA pvt == lengthL pvt + 1 + lengthR pvt
goR : Pivot a -> Maybe (Pivot a)
Move one step right.
Fails if and only if the right side is empty.
Tip: withRollback
replaces failures with no-ops (see Utilities).
goR [1 2 3 *4*] == Nothing
goR [1 *2* 3 4] == Just [1 2 *3* 4]
goL : Pivot a -> Maybe (Pivot a)
Move one step left.
Fails if and only if the left side is empty.
goRelative : Basics.Int -> Pivot a -> Maybe (Pivot a)
Move right by some number of steps. Negative numbers move left instead.
Fails if and only if the movement goes out of bounds.
goBy : Basics.Int -> Pivot a -> Maybe (Pivot a)
Alias for goRelative
.
goAbsolute : Basics.Int -> Pivot a -> Maybe (Pivot a)
Go to a specific position from the left. Starts with 0.
Fails if and only if the position given doesn't exist.
goTo : Basics.Int -> Pivot a -> Maybe (Pivot a)
Alias for goAbsolute
goToStart : Pivot a -> Pivot a
Go to starting position.
goToStart >> lengthL == always 0
goToEnd : Pivot a -> Pivot a
Go to starting position.
goToEnd >> lengthR == always 0
firstWith : (a -> Basics.Bool) -> Pivot a -> Maybe (Pivot a)
Find the first member of a pivot satisfying some predicate.
Fails if and only if there are no such members.
lastWith : (a -> Basics.Bool) -> Pivot a -> Maybe (Pivot a)
Find the last member of a pivot satisfying some predicate.
Fails if and only if there are no such members.
findR : (a -> Basics.Bool) -> Pivot a -> Maybe (Pivot a)
Find the first member to the center's right satisfying some predicate.
Fails if and only if there are no such members.
findR ((==) 3) [ 1 2 * 3 * 4 ] == Nothing
findL : (a -> Basics.Bool) -> Pivot a -> Maybe (Pivot a)
Find the first member to the center's left satisfying some predicate.
Fails if and only if there are no such members.
findL ((==) 2) [ 1 2 * 3 * 4 ] == Just [ 1 * 2 * 3 4 ]
findCR : (a -> Basics.Bool) -> Pivot a -> Maybe (Pivot a)
Like findR
, but checks the center first as well.
Fails if and only if there are no such members.
findCR ((==) 3) [ 1 2 * 3 * 4 ] == Just [ 1 2 * 3 * 4 ]
firstWith pred == goToStart >> findCR pred
findCL : (a -> Basics.Bool) -> Pivot a -> Maybe (Pivot a)
Like findL
, but checks the center first as well.
Fails if and only if there are no such members.
Now we start seeing functions that can actually change the underlying list.
setC : a -> Pivot a -> Pivot a
Replace the center.
setL : List a -> Pivot a -> Pivot a
Replace the left.
setR : List a -> Pivot a -> Pivot a
Replace the right.
appendL : a -> Pivot a -> Pivot a
Add a member to the left of the center
appendR : a -> Pivot a -> Pivot a
Add a member to the right of the center
appendGoL : a -> Pivot a -> Pivot a
Add a member to the left of the center and immediately move left.
We know that appendL >> goL
cannot really fail, but it still results in a
Maybe
type. This avoids this issue.
appendGoL >> Just == appendL >> goL
appendGoR : a -> Pivot a -> Pivot a
Add a member to the right of the center and immediately move right.
appendListL : List a -> Pivot a -> Pivot a
Like List.append
, but the right side is a pivot.
appendListL [ 8, 9 ] [ 1 2 * 3 * 4 ] == [ 8 9 1 2 * 3 * 4 ]
appendListR : List a -> Pivot a -> Pivot a
Like List.append
, but the left side is a pivot.
appendListR [ 8, 9 ] [ 1 2 * 3 * 4 ] == [ 1 2 * 3 * 4 8 9 ]
Removing is not guaranteed to work, for the simple reason that a pivot cannot be empty.
removeGoL : Pivot a -> Maybe (Pivot a)
Replace center with member nearest to the left.
Fails if and only if left side is empty.
removeGoR : Pivot a -> Maybe (Pivot a)
Replace center with member nearest to the right.
Fails if and only if right side is empty.
Switch places with other members.
switchL : Pivot a -> Maybe (Pivot a)
Switch places with member nearest to the left
Fails if and only if left side is empty
switchR : Pivot a -> Maybe (Pivot a)
Switch places with member nearest to the right
Fails if and only if right side is empty
sort : Pivot comparable -> Pivot comparable
Sort a pivot while keeping the center as center.
It does not simply sort each side separately!
sort >> getA == getA >> List.sort
getC == sort >> getC
sortWith : (a -> a -> Basics.Order) -> Pivot a -> Pivot a
Like sort
, but with a costum comparator.
sort == sortWith compare
Lists can be mapped over, and so can pivots. However, since a pivot is made up of three distinct objects at any time, you may want to apply different transformations to the different objects.
mapCLR : (a -> b) -> (a -> b) -> (a -> b) -> Pivot a -> Pivot b
Provide functions that control what happens to the center, the left members and the right members separately, and get a function that acts on pivots.
mapCRL : (a -> b) -> (a -> b) -> (a -> b) -> Pivot a -> Pivot b
Like mapCLR
, but provide the function for the right before the left.
mapCS : (a -> b) -> (a -> b) -> Pivot a -> Pivot b
Like mapCLR
, but you provide one function for both sides.
mapA : (a -> b) -> Pivot a -> Pivot b
Like mapCS
, but you provide one function for all members.
This is exactly like List.map
for the underlying list.
mapA ((==) 3) [ 1 * 2 * 3 4 ] == [ False * False * True False ]
mapC : (a -> a) -> Pivot a -> Pivot a
Like mapA
, but only the center is affected.
mapS : (a -> a) -> Pivot a -> Pivot a
Like mapA
, but the center is not affected.
mapL : (a -> a) -> Pivot a -> Pivot a
Like mapA
, but only the left is affected.
mapR : (a -> a) -> Pivot a -> Pivot a
Like mapA
, but only the right is affected.
Some List a -> List b
functions cannot be made from a -> b
functions.
This is why these maps may be of importance.
Just replace map*
with mapWhole*
to use functions on whole lists instead of values.
mapWholeCLR : (a -> b) -> (List a -> List b) -> (List a -> List b) -> Pivot a -> Pivot b
Like mapCLR
, but the functions for the left and right act on the
lists as a whole, and not on each member separately.
The lists are ordered from the center out.
mapWholeCLR ((*) 3) (List.drop 1) (List.drop 1) [ 1 2 * 3 * 4 5 ] == [ 1 * 9 * 5 ]
mapWholeCRL : (a -> b) -> (List a -> List b) -> (List a -> List b) -> Pivot a -> Pivot b
See mapWholeCLR
.
mapWholeCS : (a -> b) -> (List a -> List b) -> Pivot a -> Pivot b
See mapWholeCLR
.
mapWholeS : (List a -> List a) -> Pivot a -> Pivot a
See mapWholeCLR
.
mapWholeL : (List a -> List a) -> Pivot a -> Pivot a
See mapWholeCLR
.
mapWholeR : (List a -> List a) -> Pivot a -> Pivot a
See mapWholeCLR
.
indexAbsolute : Pivot a -> Pivot ( Basics.Int, a )
Adds indices to all values, from left to right.
Based internally on List.indexedMap
.
indexAbsolute [ 1 2 * 3 * 4 ] == [ ( 0, 1 ) ( 1, 2 ) * ( 2, 3 ) * ( 3, 4 ) ]
indexRelative : Pivot a -> Pivot ( Basics.Int, a )
Like indexAbsolute
, but relative to the center (that gets the index 0).
indexAbsolute [ 1 2 * 3 * 4 ] == [ ( -2, 1 ) ( -1, 2 ) * ( 0, 3 ) * ( 1, 4 ) ]
apply : Pivot (a -> b) -> Pivot a -> Pivot b
Apply functions in a pivot on values in another Pivot. The center gets applied to the center, and each side gets applied to each side. But how does a list of functions get applied on a list of values? Well, each function maps over the complete list of values, and then all the lists created from these applications are concatinated.
mapCLR onC onL onR == apply [ onL * onC * onR ]
reverse : Pivot a -> Pivot a
Reverse a pivot, like a list. You could also think of it as mirroring left and right.
mirror : (Pivot a -> Pivot b) -> Pivot a -> Pivot b
Reverse a function's notion of left and right. Used in many of this library's functions under the hood
mirrorM : (Pivot a -> Maybe (Pivot b)) -> Pivot a -> Maybe (Pivot b)
Reverse a possibly-failing-function's notion of left and right. Used in many of this library's functions under the hood
assert : Pivot (Maybe a) -> Maybe (Pivot a)
Takes a pivot full of possible values, and realizes it only if all
the values are real. That is, if all the values are Just a
, then we get
Just (Pivot a)
. Otherwise, we get Nothing
.
This is great for composing with the different map functions.
For example, you could define
mapAM : (a -> Maybe b) -> Pivot a -> Maybe (Pivot b)
mapAM f =
mapA f >> assert
withRollback : (a -> Maybe a) -> a -> a
Replace a possibly-failing-function with a possibly-does-nothing-function.
For example, if you try to goR
a pivot,
you may fail since there is nothing to the right.
But if you withRollback goR
a pivot,
the worst that could happen is that nothing happens.
Use it, don't abuse it. That is, only use it when it makes sense to ignore a failure, or when you are certain a possibly-failing-function cannot really fail. For example,
appendGoR == appendR >> withRollback goR