Template for implementing undo / redo in clojure

Is there an established template for implementing undo / redo functions in clojure or in fp in general?

In the OO language, I would go with a command template, but since everything is in state, I wonder if this is an idiomatic implementation of this in clojure.

Are there any libraries that could help?

+6
source share
2 answers

As with many design patterns, you can implement this as a function in clojure. It depends a little on how you represent the state in your program (refs, atom, agents) through the process, very similar.

You simply add an observer function to the agent / ref / atom of your state , which adds state to the cancellation list with each update. then your undo function just looks in the undo list. This has the nice effect of adding your to your cancellation list, allowing you to also repeat

My first impression is that ref may be the right tool for this, because you can restore them all in a coordinated way, unless, of course, you can reduce your programs to one identifier (in Clojure, the meaning of the word), then you will not need coordinated update and the agent will work.

+5
source

Well, I did it, as Arthur Ulfeldt suggested:

 (defn cmd-stack [state-ref] (let [stack (atom ['() '()])] (add-watch state-ref :cmdhistory (fn [key ref old new] (let [redo-stack '() undo-stack (conj (second @stack) old)] (reset! stack [redo-stack undo-stack])))) stack)) (defn can-redo? [stack] (not (empty? (first @stack)))) (defn can-undo? [stack] (not (empty? (second @stack)))) (defn undo! [stack state-ref] (let [redo-stack (first @stack) undo-stack (second @stack) current-state @state-ref last-state (first undo-stack)] (assert (can-undo? stack) "cannot undo") (reset! state-ref last-state) (reset! stack [(conj redo-stack current-state) (drop 1 undo-stack)]))) (defn redo! [stack state-ref] (let [redo-stack (first @stack) undo-stack (second @stack) current-state @state-ref last-state (first redo-stack)] (assert (can-redo? stack) "cannot redo") (reset! state-ref last-state) (reset! stack [(drop 1 redo-stack) (conj undo-stack current-state)]))) 

But I still do not quite understand why. With the cancellation! and repeat! the functions update the atom that is being viewed, shouldn't the observer react to this and thus ruin the command stack by returning it back to it?

+1
source

Source: https://habr.com/ru/post/911543/


All Articles