Zippers for data-structure editing with DCG state management

I have a need to compose declarative transformations on values.

“go to this key, that index, this key, and update the value there, now go to this other key, another key and update the value there …”
“now apply this transformation” … and so on.

I’m playing around with the idea of using zippers to “move around inside” a data structure, perform changes to a focused valued, and unzipping to commit changes. A zipper just keeps track of a “focused” value and a stack of where it came from.

The following is just some code I was playing with today, and looks like it’ll give me what I need.
I’m just asking for feedback to make sure I’m not reinventing a wheel or missing something obvious.

Using DCGs for state management, we have:

state(S), [S] --> [S].
state(S0, S1), [S1] --> [S0].

zip -->
    state(X, zipper(X, [])).

unzip -->
    state(zipper(X, []), X).

Assuming (for now) our structures are nested assocs, we have

% step into a substructure by key
:- op(500, fx, >:).
>:(Key) -->
    state(zipper(Focus, Where), zipper(Value, [Focus:Key|Where])),
    { get_assoc(Key, Focus, Value)}.

unzip -->
    state(zipper(NewValue, [Owner:Key|Where]), zipper(NewOwner, Where)),
    { put_assoc(Key, Owner, NewValue, NewOwner) },
    unzip.

a way to do something to the focused part of a zipper

focused(Do) -->
    state(zipper(Focus, Where), Focus),
    Do,
    state(NewFocus, zipper(NewFocus, Where)).

and a way to “commit” to a substructure change, and start anbother

andThenZip -->
    unzip, zip.

Finally we can do things like this:

% a nested assoc
example_assoc(Assoc) :-
    list_to_assoc([a-A, x-X], Assoc),
    list_to_assoc([b-1, c-1], A),
    list_to_assoc([y-10, z-10], X).

some_update -->
    zip, >:a, >:b, 
    focused((
        state(In1, Out1), {Out1 is In1 + 1}
        )),

    andThenZip, >:x, >:y,
    focused((
        state(In2, Out2), {Out2 is Out1 + In2}
        )),

    unzip.

and run it to get:

?- example_assoc(A), phrase(some_update, [A], [O]).
A = t(x,t(z,10,<,t(y,10,-,t,t),t),<,t(a,t(c,1,<,t(b,1,-,t,t),t),-,t,t),t),
O = t(x,t(z,10,<,t(y,12,-,t,t),t),<,t(a,t(c,1,<,t(b,2,-,t,t),t),-,t,t),t) ;

Have you seen

EDCG seemed designed to handle multiple named accumulators in a DCG.

I’m trying to do a declarative nested structural editing.

I’m not saying the two aren’t related, but it’s not (yet) clear to me how to get what I need from that.