Current section
Files
Jump to
Current section
Files
src/zipper@list.erl
-module(zipper@list).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/zipper/list.gleam").
-export([from_list/1, new/0, to_list/1, get_value/1, get/1, insert_left/2, insert_right/2, set_value/2, set/2, update/2, upsert/2, delete/1, is_empty/1, is_leftmost/1, is_rightmost/1, go_left/1, go_right/1]).
-export_type([zipper/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A functional zipper data structure for efficient list navigation and manipulation.\n"
"\n"
" Provides $O(1)$ operations for insertion, deletion, and updates at the current\n"
" focus position, with efficient bidirectional navigation. And Provides $O(n)$ operations for\n"
" converting between lists and zippers, where $n$ is the number of elements in the zipper.\n"
"\n"
" ## Usage\n"
" ```gleam\n"
" import zipper/list as zipper_list\n"
"\n"
" let zipper = zipper_list.from_list([1, 2, 3, 4])\n"
"\n"
" let assert Ok(zipper) = zipper_list.go_right(zipper)\n"
" let assert Ok(zipper) = zipper_list.set_value(zipper, 99)\n"
" let zipper = zipper_list.insert_left(zipper, 42)\n"
" let assert Ok(zipper) = zipper_list.go_right(zipper)\n"
" let assert Ok(zipper) = zipper_list.delete(zipper)\n"
"\n"
" zipper_list.to_list(zipper)\n"
" // => [1, 42, 99, 4]\n"
" ```\n"
).
-opaque zipper(DKM) :: {zipper, list(DKM), list(DKM)}.
-file("src/zipper/list.gleam", 59).
?DOC(
" Create a zipper from a list, with focus on the first element.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" get_value(zipper)\n"
" // => Ok(1)\n"
" ```\n"
).
-spec from_list(list(DKP)) -> zipper(DKP).
from_list(List) ->
{zipper, [], List}.
-file("src/zipper/list.gleam", 47).
?DOC(
" Create a new empty zipper.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let empty = new()\n"
" to_list(empty)\n"
" // => []\n"
" ```\n"
).
-spec new() -> zipper(any()).
new() ->
from_list([]).
-file("src/zipper/list.gleam", 71).
?DOC(
" Convert a zipper back to a regular list.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" to_list(zipper)\n"
" // => [1, 2, 3]\n"
" ```\n"
).
-spec to_list(zipper(DKS)) -> list(DKS).
to_list(Zipper) ->
_pipe = lists:reverse(erlang:element(2, Zipper)),
lists:append(_pipe, erlang:element(3, Zipper)).
-file("src/zipper/list.gleam", 110).
?DOC(
" Get the current focus value of the zipper.\n"
"\n"
" Returns `Error(Nil)` if the zipper is empty.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" get_value(zipper)\n"
" // => Ok(1)\n"
"\n"
" let empty = new()\n"
" get_value(empty)\n"
" // => Error(Nil)\n"
" ```\n"
).
-spec get_value(zipper(DKZ)) -> {ok, DKZ} | {error, nil}.
get_value(Zipper) ->
case Zipper of
{zipper, _, []} ->
{error, nil};
{zipper, _, [Current | _]} ->
{ok, Current}
end.
-file("src/zipper/list.gleam", 92).
?DOC(
" Get the current focus value of the zipper.\n"
"\n"
" **Deprecated**: Use [`get_value`](#get_value) instead.\n"
"\n"
" Returns `Error(Nil)` if the zipper is empty.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" get_value(zipper)\n"
" // => Ok(1)\n"
"\n"
" let empty = new()\n"
" get_value(empty)\n"
" // => Error(Nil)\n"
" ```\n"
).
-spec get(zipper(DKV)) -> {ok, DKV} | {error, nil}.
get(Zipper) ->
get_value(Zipper).
-file("src/zipper/list.gleam", 137).
?DOC(
" Insert a new value to the left of the current focus value.\n"
"\n"
" If the zipper is empty, the new value becomes the focus. If the zipper is not empty,\n"
" the new value is inserted before the current focus value.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper =\n"
" from_list([2, 3])\n"
" |> insert_left(1)\n"
" to_list(zipper)\n"
" // => [1, 2, 3]\n"
" get_value(zipper)\n"
" // => Ok(2)\n"
"\n"
" let empty = from_list([])\n"
" let zipper = insert_left(empty, 42)\n"
" get_value(zipper)\n"
" // => Ok(42)\n"
" ```\n"
).
-spec insert_left(zipper(DLD), DLD) -> zipper(DLD).
insert_left(Zipper, Value) ->
case Zipper of
{zipper, [], []} ->
{zipper, [], [Value]};
{zipper, Thread, Focus} ->
{zipper, [Value | Thread], Focus}
end.
-file("src/zipper/list.gleam", 157).
?DOC(
" Insert a new value to the right of the current focus value.\n"
" If the original zipper is empty, the new value becomes the focus;\n"
" otherwise, the original focus remains unchanged.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2])\n"
" let zipper = insert_right(zipper, 3)\n"
" to_list(zipper)\n"
" // => [1, 3, 2]\n"
" get_value(zipper)\n"
" // => Ok(1)\n"
" ```\n"
).
-spec insert_right(zipper(DLG), DLG) -> zipper(DLG).
insert_right(Zipper, Value) ->
case Zipper of
{zipper, _, []} ->
{zipper, erlang:element(2, Zipper), [Value]};
{zipper, _, [Head | Rest]} ->
{zipper, erlang:element(2, Zipper), [Head | [Value | Rest]]}
end.
-file("src/zipper/list.gleam", 194).
?DOC(
" Set the current focus value of the zipper list.\n"
"\n"
" Returns the previous value if successful, or `Error(Nil)` if the zipper is empty.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(updated) = set_value(zipper, 99)\n"
" to_list(updated)\n"
" // => [99, 2, 3]\n"
" ```\n"
).
-spec set_value(zipper(DLO), DLO) -> {ok, zipper(DLO)} | {error, nil}.
set_value(Zipper, New_value) ->
case Zipper of
{zipper, _, []} ->
{error, nil};
{zipper, _, [_ | Rest]} ->
{ok, {zipper, erlang:element(2, Zipper), [New_value | Rest]}}
end.
-file("src/zipper/list.gleam", 179).
?DOC(
" Set the current focus value of the zipper list.\n"
"\n"
" **Deprecated**: Use [`set_value`](#set_value) instead.\n"
"\n"
" Returns the previous value if successful, or `Error(Nil)` if the zipper is empty.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(updated) = set_value(zipper, 99)\n"
" to_list(updated)\n"
" // => [99, 2, 3]\n"
" ```\n"
).
-spec set(zipper(DLJ), DLJ) -> {ok, zipper(DLJ)} | {error, nil}.
set(Zipper, New_value) ->
set_value(Zipper, New_value).
-file("src/zipper/list.gleam", 213).
?DOC(
" Update the current focus value of the zipper list using a transformation function.\n"
"\n"
" Returns the previous value if successful, or `Error(Nil)` if the zipper is empty.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(updated) = update(zipper, fn(x) { x * 2 })\n"
" to_list(updated)\n"
" // => [2, 2, 3]\n"
" ```\n"
).
-spec update(zipper(DLT), fun((DLT) -> DLT)) -> {ok, zipper(DLT)} | {error, nil}.
update(Zipper, Updater) ->
case Zipper of
{zipper, _, []} ->
{error, nil};
{zipper, _, [Current | Rest]} ->
{ok, {zipper, erlang:element(2, Zipper), [Updater(Current) | Rest]}}
end.
-file("src/zipper/list.gleam", 253).
?DOC(
" Update or insert a value at the current focus position.\n"
"\n"
" If the zipper is empty, inserts a new value. If the zipper has a focus,\n"
" updates the current value using the transformation function.\n"
"\n"
" The updater function receives the current focus value as `Some(value)`\n"
" if present, or `None` if the zipper is empty.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let transform = fn(x) {\n"
" case x {\n"
" Some(value) -> value * 2\n"
" None -> 42\n"
" }\n"
" }\n"
"\n"
" // Update existing value\n"
" let zipper = from_list([1, 2, 3])\n"
" let zipper = upsert(zipper, transform)\n"
" to_list(zipper)\n"
" // => [2, 2, 3]\n"
"\n"
" // Insert when empty\n"
" let empty = new()\n"
" let zipper = upsert(empty, transform)\n"
" to_list(zipper)\n"
" // => [42]\n"
" ```\n"
).
-spec upsert(zipper(DLY), fun((gleam@option:option(DLY)) -> DLY)) -> zipper(DLY).
upsert(Zipper, Updater) ->
case Zipper of
{zipper, _, []} ->
Value = Updater(none),
{zipper, erlang:element(2, Zipper), [Value]};
{zipper, _, [Current | Rest]} ->
Value@1 = Updater({some, Current}),
{zipper, erlang:element(2, Zipper), [Value@1 | Rest]}
end.
-file("src/zipper/list.gleam", 290).
?DOC(
" Delete the current focus element from the zipper.\n"
"\n"
" The focus moves to the right element if it exists, otherwise to the left\n"
" element.\n"
"\n"
" Returns `Ok(zipper)` with the focus moved to the new location.\n"
" Returns `Error(Nil)` if the zipper is empty or contains only one element.\n"
"\n"
" This operation is prevented when the zipper has only one element to ensure\n"
" the zipper never becomes empty without a focus.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" // Successful deletion with multiple elements\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(zipper) = delete(zipper)\n"
" to_list(zipper)\n"
" // => [2, 3]\n"
"\n"
" // Error when trying to delete the only element\n"
" let single = from_list([42])\n"
" delete(single)\n"
" // => Error(Nil)\n"
" ```\n"
).
-spec delete(zipper(DMC)) -> {ok, zipper(DMC)} | {error, nil}.
delete(Zipper) ->
case Zipper of
{zipper, _, []} ->
{error, nil};
{zipper, [], [_]} ->
{error, nil};
{zipper, [Value | Thread], [_]} ->
{ok, {zipper, Thread, [Value]}};
{zipper, _, [_ | Focus]} ->
{ok, {zipper, erlang:element(2, Zipper), Focus}}
end.
-file("src/zipper/list.gleam", 311).
?DOC(
" Check if the zipper is empty (contains no elements).\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let empty = new()\n"
" is_empty(empty)\n"
" // => True\n"
"\n"
" let zipper = from_list([1])\n"
" is_empty(zipper)\n"
" // => False\n"
" ```\n"
).
-spec is_empty(zipper(any())) -> boolean().
is_empty(Zipper) ->
case Zipper of
{zipper, [], []} ->
true;
_ ->
false
end.
-file("src/zipper/list.gleam", 333).
?DOC(
" Check if the current focus is the leftmost element in the zipper list.\n"
"\n"
" Returns `True` if there are no elements to the left of the current focus.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" is_leftmost(zipper)\n"
" // => True\n"
"\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(moved) = go_right(zipper)\n"
" is_leftmost(moved)\n"
" // => False\n"
" ```\n"
).
-spec is_leftmost(zipper(any())) -> boolean().
is_leftmost(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
true;
_ ->
false
end.
-file("src/zipper/list.gleam", 356).
?DOC(
" Check if the current focus is the rightmost element in the zipper list.\n"
"\n"
" Returns `True` if there are no elements to the right of the current focus.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" is_rightmost(zipper)\n"
" // => False\n"
"\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(moved) = go_right(zipper)\n"
" let assert Ok(rightmost) = go_right(moved)\n"
" is_rightmost(rightmost)\n"
" // => True\n"
" ```\n"
).
-spec is_rightmost(zipper(any())) -> boolean().
is_rightmost(Zipper) ->
case erlang:element(3, Zipper) of
[] ->
true;
[_] ->
true;
_ ->
false
end.
-file("src/zipper/list.gleam", 375).
?DOC(
" Move the focus one position to the left.\n"
"\n"
" Returns a new zipper with focus moved left, or `Error(Nil)` if already at leftmost.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(moved) = go_right(zipper)\n"
" let assert Ok(returned) = go_left(moved)\n"
" get_value(returned)\n"
" // => Ok(1)\n"
" ```\n"
).
-spec go_left(zipper(DMN)) -> {ok, zipper(DMN)} | {error, nil}.
go_left(Zipper) ->
case Zipper of
{zipper, [], _} ->
{error, nil};
{zipper, [Value | Thread], Focus} ->
{ok, {zipper, Thread, [Value | Focus]}}
end.
-file("src/zipper/list.gleam", 394).
?DOC(
" Move the focus one position to the right.\n"
"\n"
" Returns a new zipper with focus moved right, or `Error(Nil)` if already at rightmost.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_list([1, 2, 3])\n"
" let assert Ok(moved) = go_right(zipper)\n"
" get_value(moved)\n"
" // => Ok(2)\n"
" ```\n"
).
-spec go_right(zipper(DMS)) -> {ok, zipper(DMS)} | {error, nil}.
go_right(Zipper) ->
case Zipper of
{zipper, _, []} ->
{error, nil};
{zipper, _, [_]} ->
{error, nil};
{zipper, Thread, [Value | Focus]} ->
{ok, {zipper, [Value | Thread], Focus}}
end.