A naive Common Lisp implementation of the Automerge runtime protocol, using the Bramble Sync Protocol's "Eager mode" for the underlying message passing. Intended for educational purposes.
avon 2d6ffc61eb Update README | 1 year ago | |
---|---|---|
README.org | 1 year ago | |
automerge.lisp | 1 year ago | |
test.lisp | 1 year ago |
A naive Common Lisp implementation of the Automerge runtime protocol, using the Bramble Sync Protocol's "Eager mode" for the underlying message passing instead of the normal Automerge sync protocol. Intended for educational purposes.
Initialize two users, `Alice` and `Bob`.
(defvar *alice* (create-schema))
(defvar *bob* (create-schema))
Exchange contact ID info:
(add-contact *alice* "bob" (getf *bob* :id))
(add-contact *bob* "alice" (getf *alice* :id))
Now let's say Alice adds a key value pair, then updates it:
(put-msg *alice* (list 0 (getf *alice* :id)) "food" "pizza")
(put-msg *alice* (list 0 (getf *alice* :id)) "food" "tofu")
Alice can now create an outgoing report of messages for Bob:
(send-eager-update *alice* (getf *bob* :id))
in this case, the report looks like this:
((5 2)
(0 NIL)
(1
(:OPID (2 7926) :OBJID (0 7926) :PROP "food" :ACTION "set value" :VALUE
"tofu" :DEP ((1 7926))))
(1
(:OPID (1 7926) :OBJID (0 7926) :PROP "food" :ACTION "set value" :VALUE
"pizza" :DEP NIL)))
Bob can process this report to recieve the updates Alice made:
(process-eager-update *bob* (getf *alice* :id)
(send-eager-update *alice* (getf *bob* :id)))
Now that Alice and Bob's states are synced, we can try and create a merge conflict:
;;;; Create a merge conflict
(put-msg *bob* (list 0 (getf *alice* :id)) "food" "salad")
(put-msg *alice* (list 0 (getf *alice* :id)) "food" "pasta")
Now the state's of both trees are diverged, let's reconcile them now using a two-way sync.
;;;; TWO WAY SYNC
;;;; note that order does not matter
(process-eager-update *alice*
(getf *bob* :id)
(send-eager-update *bob* (getf *alice* :id)))
(process-eager-update *bob*
(getf *alice* :id)
(send-eager-update *alice* (getf *bob* :id)))
Thanks to the Automerge runtime protocol which uses "Last Writer Wins" + using Contact ID as the tiebreaker, we can see that both structures show the same value of "salad" for the "food" key.
;;;; Should both return "salad"
(get-msg *alice* (list 0 (getf *alice* :id)) "food")
(get-msg *bob* (list 0 (getf *alice* :id)) "food")
You can also set a key to point to another Map object
;;;; Put objects into slots
(let ((table (put-obj *bob* (list 0 (getf *alice* :id)) "contact")))
(put-msg *bob* table "email" "someemail@email.com"))
(let ((table (get-msg *bob* (list 0 (getf *alice* :id)) "contact")))
(get-msg *bob* table "email")) ; "someemail@email.com