I sketch a possible approach. I would first define the operations that are expected to be applied to your data structure (if I understand correctly, you have already done this). Using Haskell notation (I am not familiar with F#, I hope the Haskell notation is intuitive enough for you) for lists you would have:
add :: a -> [a] -> [a]
remove :: Eq a => a -> [a] -> [a]
where I assume that
add is just the cons operation
remove is a function that removes the first occurrence of the specified element. So, in Haskell these are already standard functions:
add = (:)
remove = delete
Then I would use a data type for log entries:
data Action a = Add a | Remove a
So the source of changes to your data structure outputs a stream of actions on some specific type. If your list contains strings, you have
actions :: [Action String]
Your update function will be
update :: Eq a => Action a -> [a] -> [a]
update (Add x) = add x
update (Remove x) = remove x
- You define the elementary operations on your data structure and try to find an efficient implementation for them. You could find interesting ideas in Purely Functional Data Structures.
- You define log entries as a data type with one data constructor per operation.
- You define the source of your actions as producing a stream of log entries.
- All places in your code that need to react to actions take a stream of log entries as input and process log entries using the update function as soon as they are available.
For simplicity's sake, I have defined the source of actions as a (lazy, possibly infinite) list. This would imply that consumers of the actions would have to pull log entries from their source. In a GUI application you would rather want to push log entries from their source to the various GUI components that need to be updated.
So, that last aspect you may need to consider is how to make your application reactive. (Maybe you have already solved this in your application, I just add some hints for completeness.)
What you need is to turn your infinite list (stream) of log entries into an observable, and let the consumers of the observable apply the update function to their local copy of the data structure.
In case you are interested, there is sufficient information about reactive programming in F# on the net, see e.g. this article.