-
Notifications
You must be signed in to change notification settings - Fork 6
Cell Reference
Speaking loosely, a cell is a reactive property of a Matrix model. A model is a CLJ map with meta
suitably provisioned to support MX reactive/dataflow behavior. Cells may optionally be expressed as functions of other cells.
A simple example involving just one model, where:
-
make
produces the model map; -
cI
produces an "I"nput cell, whose value we will be able to alter imperatively; -
cF
produces a "F"ormulaic cell, which can compute its value using other cell values; and -
mget
andmswap!
provide the expected functionality, integrated with the MX engine:
(let [md (md/make
:height 2 ;; meters
:weight (cI 80) ;; kg
:bmi (cF (/ (mget me :weight)
(Math/pow (mget me :height) 2)))
:weight-status (cF (let [bmi (mget me :bmi)]
(cond
(< bmi 18.5) :underweight
(<= 18.5 bmi 24.9) :healthy
(<= 25.0 bmi 29.9) :overweight
:else :obese))))]
(is (= (mget md :bmi) 20.0))
(is (= (mget md :weight-status) :healthy))
(mswap! md :weight + 20)
(is (= (mget md :bmi) 25.0))
(is (= (mget md :weight-status) :overweight)))
n.b. Cycles in such dependencies are detected at run time and throw an exception, but see our "Possibilities" appendix.
Implementation-wise, a cell is a data structure, another CLJ map, associated with a property of a model instance. When a cell changes, dependent cells are recalculated, recursively propagating until the state tree is consistent. Think "spreadsheet".
Unlike a spreadsheet cell, MX cells can be assigned "watch" functions. These fire as soon as a cell sees a new value, and are expected to act outside the MX reactive flow. However, if wrapped in a with-change
macro, watch functions can mutate other input cells.
That describes the core cell behavior. Next we look at specific cell options and consider how each refines cell behavior.
Here are the options expected/allowed by make-cell
, the non-formulaic cell fabricator, with the more common options first:
Option | Description |
---|---|
value | the initial value for the property. Default nil ; |
watch | an optional function with signature (fn [prop me new-value prior-value cell]...)
|
ephemeral? | if true, the cell value, after being set and propagated, is reset to nil without propagating. This exists to support event-like state; |
on-quiesce | Optional. A function (fn [cell]...) to be called when a cell is being disconnected from the matrix |
unchanged-if | default is CLJ = . Optional function with signature (fn [new-value prior-value]...) |
prop | the property name. Optional. Default nil, but populated automatically by the model make function; |
input? | indicates if this value be altered by imperative code. Default true; |
debug | a keyword that can be used by the MX debug tools to filter tracing |
Same as for input cells, with these additions:
Option | Description |
---|---|
rule | aka "formula". A function (fn [cell] ...) to compute the property value. See below for cF macros that unpack the cell usefully. |
input? | Default false. If true, the formula is evaluated exactly once, without establishing dependencies, and then can be altered as if it were an input cell. This is needed where a cell destined to be an input requires initialization from other run-time state. Analog is an OO class constructor function initializing an instance based on other instance parameters. |
lazy | WIP. Possible values true (alias :always ), :until-asked (then eager), :once-asked (before then eager), false/nil. |
async? | RSN. Eventually will process a future (in some form) produced by the formula in place, assuming the value nil until the async value is obtained, then mset ting that value into the cell as an external change to an input. |
Here are some handy macros for formulaic properties of MX models:
Macro | Description |
---|---|
cF | Syntax: (cf _rule-body_) . The rule body will have access to three anaphors: me , the model; _cache , the prior value, and _cell , the cell instance. On the first evaluation, the _cache will be tiltontec.matrix.api/unbound , currently (gensym "unbound-cell-value") . |
cF+ | Syntax: (cF+ [ & _cell-options_ ] _rule-body_) , where cell-options are alternating keys and values. |
cFn | Syntax: (cf _rule-body_) . Equivalent to (cF+ [:input? true] _rule_body) . |
We collect here open items, or ideas worth exploring. The evolution of Cells has almost invariably been driven by actual application requirements, so not all possible features will be implemented when conceived in a vacuum. Instead, we record them here just to let folks know we have them on radar.
What if we want to write some rules such that A <- B <- A?
Currently that throws an exception at run time, but we can conceive supporting a circuit breaker option on a cell, which would be a function to decide a value should evaluation circle back to the same cell. This is a great case, btw, of something with a non-obvious implementation, so we want to wait for an authentic use case before acting.
For more information, DM the author @kennytilton on the #clojurians Slack, or visit the #matrix channel there.