Module Relude_SequenceZipper.WithSequence

Creates a Zipper using the given SEQUENCE as the backing implementation.

Heavily inspired by Queensland Function Programming Lab Haskell implementation, although without many of the advanced capabilities, like the ListZipperOp stuff. https://github.com/qfpl/list-zipper/blob/master/src/Data/ListZipper.hs

See also this very enlightening presentation about zippers for more background: http://data.tmorris.net/talks/zippers/0a1062fd0526d7ac1f41ade1e4db1465d311b4fd/zippers.pdf

Parameters

Signature

type t('a) =
| Zipper(S.t('a), 'a, S.t('a))
;

A Zipper type contains a sequence on the left (in reverse order, so that the head of the left sequence is treated as if it's the item immediately to the left of the focus), a focused item, and a sequence on the right.

The left sequence is reversed so that moving the focus one item to the left is an O(1) operation for list-based implementations. Prepending to an array-based implementation might not be O(1), but list is probably the more common use-case for a zipper since it has ideal performance for moving the focus to the left or right. The array-backed implementation has the left reversed too, for API consistency.

let make: a. S.t('a) => 'a => S.t('a) => t('a);

Constructs a Zipper from a left sequence, a focus, and a right sequence.

NB: the left sequence should be given in reverse - the head of the left sequence is treated as the item immediately to the left of the focus. The right sequence is in "natural" order where the head of the right sequence is treated as the item immediately to the right of the focus.

let makeWithLeft: a. S.t('a) => 'a => t('a);

Constructs a Zipper from a left sequence and a focus.

NB: the left sequence should be given in reverse - the head of the left sequence is treated as the item immediately to the left of the focus.

let makeWithRight: a. 'a => S.t('a) => t('a);

Constructs a Zipper from a focus and a right sequence.

NB: The right sequence is in "natural" order where the head of the right sequence is treated as the item immediately to the right of the focus.

let makeWithRightArray: a. 'a => array('a) => t('a);

Makes a zipper with a focus at the start and with an array at the tail end

let makeWithRightList: a. 'a => list('a) => t('a);

Makes a zipper with a focus at the start and with a list at the tail end

let makeWithFocus: a. 'a => t('a);

Constructs a zipper with empty left and right sequence, and a single focus.

let fromSequence: a. S.t('a) => option(t('a));

Creates a Zipper from a sequence putting the focus on the first item. If the sequence is empty, None is returned.

let fromArray: a. array('a) => option(t('a));

Makes a zipper from a non-empty list. Returns None if empty.

let fromList: a. list('a) => option(t('a));

Makes a zipper from a non-empty list. Returns None if empty.

let fromNonEmptyArray: a. Relude_NonEmpty.Array.t('a) => t('a);

Makes a zipper from a NonEmptyArray, with the focus on the first item

let fromNea: Relude_NonEmpty.Array.t('a) => t('a);

Makes a zipper from a NonEmptyArray, with the focus on the first item

Alias for fromNonEmptyArray

let fromNonEmptyList: a. Relude_NonEmpty.List.t('a) => t('a);

Makes a zipper from a NonEmptyList, with the focus on the first item

let fromNel: Relude_NonEmpty.List.t('a) => t('a);

Makes a zipper from a NonEmptyList, with the focus on the first item

Alias for fromNonEmptyList

let map: a b. ('a => 'b) => t('a) => t('b);

Maps a pure function over the values of a Zipper

module Functor: BsBastet.Interface.FUNCTOR with type Functor.t('a) = t('a);
include { ... };
module BsFunctorExtensions: { ... };
let flipMap: Functor.t('a) => ('a => 'b) => Functor.t('b);
let void: Functor.t('a) => Functor.t(unit);
let voidRight: 'a => Functor.t('b) => Functor.t('a);
let voidLeft: Functor.t('a) => 'b => Functor.t('b);
let flap: Functor.t(('a => 'b)) => 'a => Functor.t('b);
let apply: a b. t(('a => 'b)) => t('a) => t('b);

Maps a Zipper of functions over a Zipper

Implementation taken from: https://github.com/qfpl/list-zipper/blob/master/src/Data/ListZipper.hs#L151 At the time of this writing, I don't personally understand why it's implemented this way.

module Apply: BsBastet.Interface.APPLY with type Apply.t('a) = t('a);
include { ... };
module BsApplyExtensions: { ... };
let applyFirst: Apply.t('a) => Apply.t('b) => Apply.t('a);
let applySecond: Apply.t('a) => Apply.t('b) => Apply.t('b);
let map2: ('a => 'b => 'c) => Apply.t('a) => Apply.t('b) => Apply.t('c);
let map3: ('a => 'b => 'c => 'd) => Apply.t('a) => Apply.t('b) => Apply.t('c) => Apply.t('d);
let map4: ('a => 'b => 'c => 'd => 'e) => Apply.t('a) => Apply.t('b) => Apply.t('c) => Apply.t('d) => Apply.t('e);
let map5: ('a => 'b => 'c => 'd => 'e => 'f) => Apply.t('a) => Apply.t('b) => Apply.t('c) => Apply.t('d) => Apply.t('e) => Apply.t('f);
let tuple2: Apply.t('a) => Apply.t('b) => Apply.t(('a, 'b));
let tuple3: Apply.t('a) => Apply.t('b) => Apply.t('c) => Apply.t(('a, 'b, 'c));
let tuple4: Apply.t('a) => Apply.t('b) => Apply.t('c) => Apply.t('d) => Apply.t(('a, 'b, 'c, 'd));
let tuple5: Apply.t('a) => Apply.t('b) => Apply.t('c) => Apply.t('d) => Apply.t('e) => Apply.t(('a, 'b, 'c, 'd, 'e));
let mapTuple2: ('a => 'b => 'c) => (Apply.t('a), Apply.t('b)) => Apply.t('c);
let mapTuple3: ('a => 'b => 'c => 'd) => (Apply.t('a), Apply.t('b), Apply.t('c)) => Apply.t('d);
let mapTuple4: ('a => 'b => 'c => 'd => 'e) => (Apply.t('a), Apply.t('b), Apply.t('c), Apply.t('d)) => Apply.t('e);
let mapTuple5: ('a => 'b => 'c => 'd => 'e => 'f) => (Apply.t('a), Apply.t('b), Apply.t('c), Apply.t('d), Apply.t('e)) => Apply.t('f);
let pure: a. 'a => t('a);

Lifts a pure value into a Zipper, as the focused value.

Alias for makeWithFocus

Note: QFPL implementation has repeat a on the left and right sides. At the time of this writing, I don't understand why it's implemented this way. I'm not sure if we can implement it this way in a strict language.

module Applicative: BsBastet.Interface.APPLICATIVE with type Applicative.t('a) = t('a);
include { ... };
module BsApplicativeExtensions: { ... };
let liftA1: ('a => 'b) => Applicative.t('a) => Applicative.t('b);
let when_: bool => Applicative.t(unit) => Applicative.t(unit);
let unless: bool => Applicative.t(unit) => Applicative.t(unit);
let all: list(Applicative.t('a)) => Applicative.t(list('a));
let foldLeft: ('b => 'a => 'b) => 'b => t('a) => 'b;

Folds a zipper from left-to-right using an accumulator

let foldRight: ('a => 'b => 'b) => 'b => t('a) => 'b;

Folds a zipper from right-to-left using an accumulator

module Foldable: BsBastet.Interface.FOLDABLE with type Foldable.t('a) = t('a);
include { ... };
module BsFoldableExtensions: { ... };
let any: ('a => bool) => Foldable.t('a) => bool;
let all: ('a => bool) => Foldable.t('a) => bool;
let containsBy: ('a => 'a => bool) => 'a => Foldable.t('a) => bool;
let contains: (module BsBastet.Interface.EQ with type t = 'a) => 'a => Foldable.t('a) => bool;
let indexOfBy: ('a => 'a => bool) => 'a => Foldable.t('a) => option(int);
let indexOf: (module BsBastet.Interface.EQ with type t = 'a) => 'a => Foldable.t('a) => option(int);
let minBy: ('a => 'a => BsBastet.Interface.ordering) => Foldable.t('a) => option('a);
let min: (module BsBastet.Interface.ORD with type t = 'a) => Foldable.t('a) => option('a);
let maxBy: ('a => 'a => BsBastet.Interface.ordering) => Foldable.t('a) => option('a);
let max: (module BsBastet.Interface.ORD with type t = 'a) => Foldable.t('a) => option('a);
let countBy: ('a => bool) => Foldable.t('a) => int;
let length: Foldable.t('a) => int;
let size: Foldable.t('a) => int;
let count: Foldable.t('a) => int;
let forEach: ('a => unit) => Foldable.t('a) => unit;
let forEachWithIndex: ('a => int => unit) => Foldable.t('a) => unit;
let find: ('a => bool) => Foldable.t('a) => option('a);
let findWithIndex: ('a => int => bool) => Foldable.t('a) => option('a);
let toList: Foldable.t('a) => list('a);
let toArray: Foldable.t('a) => array('a);
module FoldableSemigroupExtensions: (S: BsBastet.Interface.SEMIGROUP) => { ... };
module FoldableMonoidExtensions: (M: BsBastet.Interface.MONOID) => { ... };
let foldMap: (module BsBastet.Interface.MONOID with type t = 'a) => ('b => 'a) => Foldable.t('b) => 'a;
let foldWithMonoid: (module BsBastet.Interface.MONOID with type t = 'a) => Foldable.t('a) => 'a;
let intercalate: (module BsBastet.Interface.MONOID with type t = 'a) => 'a => Foldable.t('a) => 'a;
module FoldableApplicativeExtensions: (A: BsBastet.Interface.APPLICATIVE) => { ... };
module FoldableMonadExtensions: (M: BsBastet.Interface.MONAD) => { ... };
module FoldableEqExtensions: (E: BsBastet.Interface.EQ) => { ... };
module FoldableOrdExtensions: (O: BsBastet.Interface.ORD) => { ... };
module type TRAVERSABLE_F = (A: BsBastet.Interface.APPLICATIVE) => BsBastet.Interface.TRAVERSABLE with type TRAVERSABLE_F.t('a) = t('a) and type TRAVERSABLE_F.applicative_t('a) = A.t('a);
module Traversable: TRAVERSABLE_F;
let toSequence: a. t('a) => S.t('a);

Converts the Zipper into a Sequence

let toArray: a. t('a) => array('a);

Converts the Zipper into an array

TODO: not needed with Foldable extensions

let toList: a. t('a) => list('a);

Converts the Zipper into a list

TODO: not needed with Foldable extensions

let toNonEmptyArray: a. t('a) => Relude_NonEmpty.Array.t('a);

Converts the Zipper into a NonEmptyArray

let toNea: a. t('a) => Relude_NonEmpty.Array.t('a);

Converts the Zipper into a NonEmptyArray

Alias for toNonEmptyArray

let toNonEmptyList: a. t('a) => Relude_NonEmpty.List.t('a);

Converts the Zipper into a NonEmptyList

let toNel: a. t('a) => Relude_NonEmpty.List.t('a);

Converts the Zipper into a NonEmptyList

Alias for toNonEmptyList

let concatWithKeepLeftFocus: prefix:t('a) => t('a) => t('a);

Concatenates two Zippers, keeping the focus from the left Zipper

let concat: prefix:t('a) => t('a) => t('a);

Concatenates two Zippers, keeping the focus from the left Zipper

Alias for concatWithKeepLeftFocus

let concatWithKeepRightFocus: prefix:t('a) => t('a) => t('a);

Concatenates two Zippers, keeping the focus from the right Zipper

module Semigroup_Any: BsBastet.Interface.SEMIGROUP_ANY with type Semigroup_Any.t('a) = t('a);
let reverse: a. t('a) => t('a);

Reverses the Zipper

let zipWith: a b c. ('a => 'b => 'c) => t('a) => t('b) => t('c);

Zips two zippers together pair-wise using the given function

let zip: a b. t('a) => t('b) => t(('a, 'b));

Zips two zippers together in pairs.

let zipWithIndex: a. t('a) => t(('a, int));

Pairs each item in the zipper with it's index as if the zipper was converted to a list or array.

let getFocus: a. t('a) => 'a;

Gets the item at the focus

let setFocusBy: a. ('a => 'a) => t('a) => t('a);

Modifies the focus value with the given function

let setFocus: a. 'a => t('a) => t('a);

Sets a new value at the focus

let getLeft: a. t('a) => S.t('a);

Gets the sequence to the left of the focus (in the unchanged reverse order in which its stored)

let setLeft: a. S.t('a) => t('a) => t('a);

Sets the sequence to the left of the focus. Note: the given sequence should be in reverse so that the head of the sequence is the item that should immediately to the left of the focus.

let getLeftInOrder: a. t('a) => S.t('a);

Gets the sequence to the left of the focus (in its sequential order, i.e. reversed from how its stored in the zipper)

let setLeftFromInOrder: a. S.t('a) => t('a) => t('a);

Sets the sequence to the left of the focus from an in-order sequence. I.e. the given sequence will be reversed by this function when stored in the new zipper.

let getRight: a. t('a) => S.t('a);

Gets the sequence to the right of the focus (order is in order, i.e. not changed)

let setRight: a. S.t('a) => t('a) => t('a);

Sets the sequence to the right of the focus (order is not changed)

let peekLeft: a. t('a) => option('a);

Gets the item to the immediate left of the focus

let peekRight: a. t('a) => option('a);

Gets the item to the immediate right of the focus

let isAtStart: a. t('a) => bool;

Indicates if the focus is at the start of the zipper

let isAtEnd: a. t('a) => bool;

Indicates if the focus is at the end of the zipper

let isAtIndex: a. int => t('a) => bool;

Indicates if the focus is at the given index of the zipper

let isAtItemBy: a. ('a => 'a => bool) => 'a => t('a) => bool;

Indicates if the focus is at the item, based on the given equality function

let isAtItem: (module BsBastet.Interface.EQ with type t = 'a) => 'a => t('a) => bool;

Indicates if the focus is at the item, based on the given EQ module

let moveLeft: a. t('a) => option(t('a));

Moves the focus one item to the left. If there are no items to the left, returns None.

let moveRight: a. t('a) => option(t('a));

Moves the focus one item to the right. If there are no items to the right, returns None.

let moveLeftWithClamp: a. t('a) => t('a);

Moves the focus one item to the left, unless we are at the start.

let moveRightWithClamp: a. t('a) => t('a);

Moves the focus one item to the right, unless we are at the end.

let moveStart: a. t('a) => t('a);

Moves the focus to the start of the zipper

let moveEnd: a. t('a) => t('a);

Moves the focus to the end of the zipper

let moveLeftWithWrap: a. t('a) => t('a);

Moves the focus one item to the left, wrapping to the end if we are currently at the start.

let moveRightWithWrap: a. t('a) => t('a);

Moves the focus one item to the right, wrapping to the start if we are currently at the end.

let moveLeftTimes: a. int => t('a) => option(t('a));

Moves the focus a number of times to the left. If the count is out of range, None is returned

let moveRightTimes: a. int => t('a) => option(t('a));

Moves the focus a number of times to the right. If the count is out of range, None is returned

let moveLeftTimesWithClamp: a. int => t('a) => t('a);

Moves the focus a number of times to the left. If the count is out of range, the focus is moved to the start.

let moveRightTimesWithClamp: a. int => t('a) => t('a);

Moves the focus a number of times to the right. If the count is out of range, the focus is moved to the start.

let moveToIndex: a. int => t('a) => option(t('a));

Moves the focus to the given index. If the index is out of range, None is returned.

let moveToIndexWithMod: a. int => t('a) => t('a);

Moves the focus to the given index modulus the length of the zipper.

let moveToIndexWithClamp: a. int => t('a) => t('a);

Moves the focus to the given index, but no farther than the start or end if the index is out of range in either direction.

let findLeftBy: ?⁠checkFocus:bool => ('a => bool) => t('a) => Relude_Option.Monad.t(t('a));

Finds the first item that satisfies the given predicate, and returns a zipper focused on that item.

Note this only searches the focus and the left side of the zipper.

let findRightBy: ?⁠checkFocus:bool => ('a => bool) => t('a) => Relude_Option.Monad.t(t('a));

Finds the first item that satisfies the given predicate, and returns a zipper focused on that item.

Note this only searches the focus and the right side of the zipper.

let findBy: ?⁠checkFocus:bool => ('a => bool) => t('a) => option(t('a));

Finds the first item that satisfies the given predicate, and returns a zipper focused on that itme. The left side is searched first, then the right.

let findItemLeftBy: ?⁠checkFocus:bool => ('a => 'a => bool) => 'a => t('a) => option(t('a));

Finds the given item at the focus or to the left, using the given eq function.

let findItemRightBy: ?⁠checkFocus:bool => ('a => 'a => bool) => 'a => t('a) => option(t('a));

Finds the given item at the focus or to the right, using the given eq function.

let findItemBy: ?⁠checkFocus:bool => ('a => 'a => bool) => 'a => t('a) => option(t('a));

Finds the given item using the given eq function. Searches the left side first, then the right.

let findItemLeft: (module BsBastet.Interface.EQ with type t = 'a) => ?⁠checkFocus:bool => 'a => t('a) => option(t('a));

Finds the given item in the left side, using the given EQ module.

let findItemRight: (module BsBastet.Interface.EQ with type t = 'a) => ?⁠checkFocus:bool => 'a => t('a) => option(t('a));

Finds the given item in the right side, using the given EQ module.

let findItem: (module BsBastet.Interface.EQ with type t = 'a) => ?⁠checkFocus:bool => 'a => t('a) => option(t('a));

Finds the given item using the given EQ module. The left side is searched first, then the right.

let insertWithPushLeft: a. 'a => t('a) => t('a);

Inserts a new item at the focus, and pushes the previous focus to the left side

let insertWithPushRight: a. 'a => t('a) => t('a);

Inserts a new item at the focus, and pushes the previous focus to the right side

let deleteWithPullLeft: a. t('a) => option(t('a));

Deletes the item at the focus, and pulls a value from the left side into focus.

If there is no value on the left, None is returned.

let deleteWithPullRight: a. t('a) => option(t('a));

Deletes the item at the focus, and pulls a value from the right side into focus.

If there is no value on the right, None is returned.

let deleteWithPullLeftOrRight: a. t('a) => option(t('a));

Deletes the item at the focus, and tries to pull an item from the left into focus. If there is no item on the left, it tries to pull an item from the right. If there is no item on the right, None is returned.

let showBy: a. ('a => string) => t('a) => string;

Converts a Zipper to a string using the given function

let show: (module BsBastet.Interface.SHOW with type t = 'a) => t('a) => string;

Converts a Zipper to a string using the given SHOW module

module Show: (ShowA: BsBastet.Interface.SHOW) => { ... };
let eqBy: a. ('a => 'a => bool) => t('a) => t('a) => bool;

Compares two Zippers for length and pair-wise equality

let eq: (module BsBastet.Interface.EQ with type t = 'a) => t('a) => t('a) => bool;

Compares two lists for length and pair-wise equality using the given EQ module.

module Eq: (EqA: BsBastet.Interface.EQ) => { ... };
module Infix: { ... };