I can't find any tutorials either, but I think I understand this well enough to explain.
We first need to cobble together a type to represent our editors current state, for now will just do
type EditorState = Int
Then we can give this to our Undo
monad
type Editor = Undo EditorState
Now we can do something like
fakeUser = do
put 1 -- Current state is 1
put 2 -- Current state is 2, 1 is saved in undo buffer
put 3 -- Current state is 3, 1 and 2 are saved
undo -- Move 3 to redo buffer, pop 2 off of undo buffer, current state is 2
redo -- reverse of the above, current state is 3
So you can think of this as a cursor in a list*, and we can move back and forth in this list where going left means we "undo" things and going right means we "redo" things. Using put
sticks an element into the list at the current point and moves the old element to the undo buffer.
I would recommend not relying on the MonadState
instance to do insertions because it hints a bit too much at the internals of Undo
and it complicates things if we wanted to track other state through our editor.
However, this is a pretty simple piece of code that you could update to not use MonadState
, get rid of the datatype contexts, remove the dependency on -f-glasgow-extensions
which is frowned upon and then upload to hackage as a simple library if you have a bit of time.
*The name of such a structure is a "zipper"