SkillAgentSearch skills...

Clobber

A simpler alternative to object prevalence for Common Lisp.

Install / Use

/learn @robert-strandh/Clobber
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

  • Explanation Clobber is an alternative to so-called "object prevalence", and in particular to [[https://cl-prevalence.common-lisp.dev/][cl-prevalence]].

Clobber is both simpler, more flexible, and more robust than systems based on object prevalence.

** Simplicity Clobber is simpler because we do not take any snapshots at all. Other systems typically use a combination of transaction logs and snapshots. Clobber uses only a transaction log which is always read into an empty system.

** Flexibility Clobber is more flexible because the system itself does not define the format of a transaction. Client code can save transactions as Lisp lists, as instances of standard-object, or anything else that can be serialized. It is also more flexible because transactions can contain any object that can be serialized, including model objects. With this method, client code does not need to manipulate "manually created pointers" such as social security numbers, account numbers, membership numbers, etc. whenever it needs to execute a transaction. Instead it can use the model objects themselves such as people, accounts, automobiles and whatnot.

** Robustness Clobber is more robust because serialization of instances of (subclasses of) =standard-object= is not accomplished based on slots. Clobber considers slots to be implementation details. In other object prevalence systems, whenever the model evolves, the serialization might no longer be valid. In contrast, Clobber serializes instances of =standard-object= as a list of pairs, each one consisting of an initarg and a value. These pairs can be handled by client code in any way it sees fit. They can be handled by an =:initarg=, by =initialize-instance=, or they can be ignored. The downside of the Clobber method is that client code must specify these pairs in the form of an initarg and the name of an accessor function to be called to obtain the value used for the initarg. This inconvenience is however relatively minor, especially considering the additional robustness it buys in terms of less sensitivity to changes in the model classes.

** Design At the heart of Clobber is a mechanism for serializing objects that preserves object identity, much like the reader macros ~#=~ and ~##~, except that Clobber detects sharing within the entire transaction log, not only within a single transaction. This mechanism is what makes it possible for client code to put any old object in a transaction, while making sure that sharing is preserved.

** Examples Two examples are included - see files [[file:Documentation/demo/demo1.lisp][demo1.lisp]], [[file:Documentation/demo/demo2.lisp][demo2.lisp]] and [[file:Documentation/demo/demo3.lisp][demo3.lisp]]

To run the demos -

#+BEGIN_SRC lisp (ql:quickload :clobber) (in-package :clobber-demo/demo1) #+(or)(in-package :clobber-demo/demo2) #+(or)(in-package :clobber-demo/demo3)

(delete-database) ; clean up (do-and-see) ; see what the database file contains after the execution of transactions #| sample output: "#2!(NEW-BANK . #3!(#4![BANK] . NIL)) #5!(NEW-BANK . #6!(#7![BANK] . NIL)) #8!(ADD-CUSTOMER . #9!(#10![PERSON :NAME #11!"Jane"] . #12!(#4^ . NIL))) #13!(ADD-CUSTOMER . #14!(#10^ . #15!(#7^ . NIL))) #16!(ADD-CUSTOMER . #17!(#18![PERSON :NAME #19!"Bill"] . #20!(#4^ . NIL))) #21!(ADD-ACCOUNT . #22!(#23![ACCOUNT :HOLDER #10^] . #24!(#4^ . NIL))) #25!(ADD-ACCOUNT . #26!(#27![ACCOUNT :HOLDER #10^] . #28!(#7^ . NIL))) #29!(ADD-ACCOUNT . #30!(#31![ACCOUNT :HOLDER #18^] . #32!(#4^ . NIL))) #33!(DEPOSIT . #34!(100 . #35!(#23^ . NIL))) #36!(DEPOSIT . #37!(200 . #38!(#27^ . NIL))) #39!(DEPOSIT . #40!(300 . #41!(#31^ . NIL))) #42!(WITHDRAW . #43!(10 . #44!(#31^ . NIL))) #45!(TRANSFER . #46!(20 . #47!(#27^ . #48!(#23^ . NIL)))) " |# ;; (reload-database-and-see) ; see that banks has the data freshly revived from the database file. #| sample output: ((:BANK-ID 45 :ACCOUNTS ((:ACCOUNT-ID 52 :PARENT-BANK-ID 45 :BALANCE 180 :HOLDER <Jane>)) :CUSTOMERS (<Jane>)) (:BANK-ID 65 :ACCOUNTS ((:ACCOUNT-ID 73 :PARENT-BANK-ID 65 :BALANCE 300 :HOLDER <Bill>) (:ACCOUNT-ID 89 :PARENT-BANK-ID 65 :BALANCE 120 :HOLDER <Jane>)) :CUSTOMERS (<Bill> <Jane>))) |#

#+END_SRC

** License Clobber is in the public domain in countries where it is possible to place works in the public domain explicitly. In other countries, we will distribute Clobber according to a license that lets the user do whatever he or she pleases with the code.

** Contact Send comments to robert.strandh@gmail.com

A manual might be written one day.

  • How to use Clobber
  1. If your application objects are instances of (subclasses of) =standard-object=, use =clobber:define-save-info= to tell Clobber how to serialize themr.

  2. Determine the data structure your application will use to represent each transaction, and how the application will restore state from each transaction.

    e.g. a transaction could be a list whose =car= is a function and whose =cdr= is a list of arguments to the function. State can be restored from such a transaction through =(lambda (txn) (apply (first txn) (rest txn)))=

  3. When you update your application state -

    1. Use =clobber:with-transaction-log= to create a transaction log.

    2. Within the body of =clobber:with-transaction-log=, use =clobber:log-transaction= to persist the changes to your application state. You may wish to define a helper function or macro for this.

If =clobber:with-transaction-log= is for some reason unsuitable -

  1. Use =clobber:open-transaction-log= to create a transaction log, usually stored in a special variable.

  2. Use =clobber:log-transaction= to persist changes to your application state.

  3. When the transaction log is no longer required, close it using =clobber:close-transaction-log=.

  • Reference *** =define-save-info (type &body save-info)= :macro: *** =with-transaction-log ((var file function) &body forms)= :macro: *** =open-transaction-log (filename function)= :function: *** =log-transaction (transaction transaction-log)= :function: *** =close-transaction-log (transaction-log)= :function: *** =transaction-log-open-p (transaction-log)= :function:
View on GitHub
GitHub Stars54
CategoryDevelopment
Updated5mo ago
Forks7

Languages

Common Lisp

Security Score

77/100

Audited on Oct 12, 2025

No findings