Skip to content

kchanqvq/lwcells

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 

Repository files navigation

LWCELLS - Light Weight Cells

LWCELLS is a dataflow extension to Common Lisp. It maintains a consistent state of cells according to functions specifying their relation. LWCELLS is designed to be simple, clean, compositional and flexible.

Tutorial

Basic usage:

(use-package :lwcells)
(defparameter *cell-1* (cell 1))
(defparameter *cell-2* (cell 2))
(defparameter *cell-3* (cell (+ (cell-ref *cell-1*) (cell-ref *cell-2*))))
(cell-ref *cell-3*) ; => 3
(setf (cell-ref *cell-2*) 5)
(cell-ref *cell-3*) ; => 6

Hackers’ note: The body of each cell form is wrapped into a function and assigned to the cell-function slot of constructed cell object (actually, a lazy-cell). At any moment, cell with non-nil cell-function will ensure their observed value matches the result of running cell-function. If you explicitly (setf cell-ref) a cell, its cell-function is removed to prevent it from running again and overwrite the explicitly assigned value.

Fancier syntax sugar that does roughly the same thing as above:

(defcell *cell-1* 1)
(defcell *cell-2* 2)
(defcell *cell-3* (+ *cell-1* *cell-2*))

defcell have the additional nicety that when you redefine a cell, if an existing cell object is to be overwritten, such cell is also deactivated so that observers (to be explained!) defined on the old cell stop making noises.

Hackers’ note: Under the hood, this stores the cell objects in variable like *cell-1*-cell, and defines symbols like *cell-1* themselves as symbol macros.

There’s also let-cell and let*-cell that does the similiar for lexical variables.

Observers:

(defcell *cell-1* 1)
(defcell *cell-2* 2)
(defcell *cell-3* (+ *cell-1* *cell-2*))
(defun report-assign (cell)
  (let ((*print-circle* t))
    (format t "Assigning ~a to ~a." (cell-ref cell) cell)))
(add-observer *cell-3*-cell 'report-assign)
(setf *cell-2* 4)
;; => Assigning 5 to #1=#S(CELL ...)

Hackers’ note: Under the hood, observers are implemented as observer-cell, a special kind of cell whose cell-ins never change.

Convenience for CLOS:

(defmodel item () ((weight :cell 0 :initarg :weight)))
(defmodel container ()
  ((items :cell nil)
   (weight :cell (reduce #'+ (items self) :key #'weight))))
(defvar *container* (make-instance 'container))
(weight *container*) ; => 0
(push (make-instance 'item :weight 10) (items *container*))
(weight *container*) ; => 10

Hackers’ note: Under the hood, defmodel expand into a defclass, an initialize-instance method to store cell objects into slots, and some accessors method to read/write values from cell objects in the slots. You can then use the accessors method to transparently access reactive values. To get and manipulate the underlying cell objects, use slot-value.

defmodel doesn’t use any MOP magic, and is fully compatible with standard CLOS classes.

Related works

cells
This is very powerful, but also very complicated. AFAIU cells are also always attached to slots and don’t exist on their own. I might be wrong – I don’t understand all the code!
computed-class
A bit more complicated than lwcells. The syntax is also a bit less sweet, requiring lots of computed-as everywhere.

Documentation

Read the source code! There isn’t lots of code.

About

Light Weight Cells

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published