Awesome
Zipper
Generic zipper implementation in Erlang.
Zippers: what are they good for?
Zippers let you traverse immutable data structures with ease and flexibility.
Contact Us
If you find any bugs or have a problem while using this library, please open an issue in this repo (or a pull request :)).
And you can check all of our open-source projects at inaka.github.io
Usage
For a map tree structure like the following:
Root = #{type => planet,
attrs => #{name => "Earth"},
children => [
#{type => continent,
attrs => #{name => "America"},
children => [
#{type => country,
attrs => #{name => "Argentina"},
children => []},
#{type => country,
attrs => #{name => "Brasil"},
children => []}
]
},
#{type => continent,
attrs => #{name => "Europe"},
children => [
#{type => country,
attrs => #{name => "Sweden"},
children => []},
#{type => country,
attrs => #{name => "England"},
children => []}
]
}
]
},
You can build a zipper by providing three simple functions:
IsBranchFun
: takes a node and returnstrue
if it is a branch node orfalse
otherwise.ChildrenFun
: takes a node and returns a list of its children.MakeNodeFun
: takes a node and a list of children and returns a new node containing the supplied list as children.
This is an example of how you would define a zipper and then use it to traverse the map tree structure above:
%% Create the zipper
IsBranchFun = fun
(#{children := [_ | _]}) -> true;
(_) -> false
end,
ChildrenFun = fun(Node) -> maps:get(children, Node) end,
MakeNodeFun = fun(Node, Children) -> Node#{children => Children} end,
Zipper = zipper:new(fun is_map/1, ChildrenFun, MakeNodefun, Root),
%% Traverse the zipper with next
Zipper1 = zipper:next(Zipper),
Zipper2 = zipper:next(Zipper),
%% Get the current zipper node
Argentina = zipper:node(Zipper2).
io:format("~p", [Argentina]),
%%= #{type => country,
%%= attrs => #{name => "Argentina"},
%%= children => []}
%% Go up and get the node
Zipper3 = zipper:up(Zipper2).
America = zipper:node(Zipper2).
io:format("~p", [America]),
%%= #{type => country,
%%= attrs => #{name => "America"},
%%= children => [#{...}, #{...}]}
Tests
Circular dependency in test environment (Katana Test ->
Elvis Core -> Zipper) is
fixed by including Zipper as a dep in the test profile in rebar.config
...
{profiles, [
{test, [
{deps, [
%% The tag will be replaced by the rebar.config.script
{zipper, {git, "https://github.com/inaka/zipper.git", {tag, "irrelevant"}}},
...
]}
]}
]}.
...
but then, we still replace the tag with the current branch. This is done in rebar.config.script
.
Therefore, it's really important to have the branch updated and pushed to github before running the
tests with rebar3 ct
.