G'day,
I think my mental model of Cells is wrong. Perhaps someone can help.
I have two model classes:
(defmodel model () ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defmodel view () ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
What I would like to do is to connect the item-index slot of an instance of MODEL to the item-index in an instance of VIEW. i.e.
(defun connect-model-and-view (model view) (setf (item-index view) (c? (item-index model))))
which does not appear to be possible.
I have navigated around the issue by introducing a third model class, CONTROLLER, that has an item-index slot initialised with (c? (item-index (^model))) and an observer that updates the view. However, I am wondering if this is the correct way as I seem to require inserting WITH-INTEGRITY in the observer.
Thanks Mark
Hi Mark,
how about:
;;; ------------------------ ;;; *** Model MY-MODEL *** ;;; ------------------------
(defmd my-model () item-index :item-index (c-in 0))
(defobserver item-index ((self my-model)) (when new-value (format *debug-io* "~%~S: New value for slot item-index => ~S." self (item-index self))))
(defmacro mk-model (id) `(make-instance 'my-model :fm-parent *parent* :md-name ,id))
;;; ----------------------- ;;; *** Model MY-VIEW *** ;;; -----------------------
(defmd my-view () item-index)
(defmacro mk-view (id model-id) `(make-instance 'my-view :fm-parent *parent* :item-index (c? (let ((model (fm^ ,model-id))) ;; -> fm^ searches for model in the current family (item-index model))) :md-name ,id))
(defobserver item-index ((self my-view)) (when new-value (format *debug-io* "~%~S: New value for slot item-index => ~S." self (item-index self))))
;;; --------------------------- ;;; *** Family CONTROLLER *** ;;; ---------------------------
(defmd my-controller (family) )
(defmacro mk-controller (id view-id model-id) `(make-instance 'my-controller :kids (c? (the-kids (mk-model ,model-id) (mk-view ,view-id ,model-id))) :md-name ,id))
;;; ----------------- ;;; *** TESTING *** ;;; -----------------
(defun controller-test ()
(let* ((self (mk-controller :controller :view :model)) (model (fm-find-kid self :model)) (view (fm-find-kid self :view)))
(setf (item-index model) 1)
(values)))
Then:
CL-USER > (controller-test)
MODEL: New value for slot item-index => 0. VIEW: New value for slot item-index => 0. MODEL: New value for slot item-index => 1. VIEW: New value for slot item-index => 1.
?
Cheers Frank
Am 03.10.2012 um 03:47 schrieb Mark Cox:
G'day,
I think my mental model of Cells is wrong. Perhaps someone can help.
I have two model classes:
(defmodel model () ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defmodel view () ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
What I would like to do is to connect the item-index slot of an instance of MODEL to the item-index in an instance of VIEW. i.e.
(defun connect-model-and-view (model view) (setf (item-index view) (c? (item-index model))))
which does not appear to be possible.
I have navigated around the issue by introducing a third model class, CONTROLLER, that has an item-index slot initialised with (c? (item-index (^model))) and an observer that updates the view. However, I am wondering if this is the correct way as I seem to require inserting WITH-INTEGRITY in the observer.
Thanks Mark
cells-devel site list cells-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/cells-devel
Hi Frank,
Thanks for your example. My apologies for requiring you to repeat your answer from last month. I found your post not long after I posted.
From your example, and other examples that use the FAMILY class, the KIDS are instantiated at the same time as the FAMILY instance. In my case, the KIDS are instantiated at different times and then "connected" in the future. I have an attempt at the end of the email which is based off your example. It does not work, and I am not sure why.
I noticed in your examples that you use macros. Is there something significant about that?
Thanks Mark
(defpackage "MODEL" (:use "COMMON-LISP" "CELLS")) (in-package "MODEL")
;; model
(defmodel my-model (model) ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defun make-model (id) (make-instance 'my-model :md-name id))
;; view
(defmodel my-view (model) ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defun make-view (id) (make-instance 'my-view :md-name id))
(defobserver item-index ((object my-view) new-value) (format t "~&VIEW item-index ~A~%" new-value))
;; controller
(defmodel my-controller (family) ((model :initarg :model :accessor model) (view :initarg :view :accessor view)))
(defun make-controller (model view) (setf (fm-parent model) :root (fm-parent view) :root) (let ((rv (make-instance 'my-controller :model (c-in model) :view (c-in view) :kids (c? (the-kids model view)) :md-name :root)))
(setf (item-index view) (c? (let ((m (model (fm^ :root)))) (item-index m)))) rv))
;; test
(defun test () (let* ((model (make-model :model)) (view (make-view :view)) (controller (make-controller model view))) (setf (item-index model) 2) (item-index view)))
Hi Mark,
Am 04.10.2012 um 00:06 schrieb Mark Cox:
Hi Frank,
Thanks for your example. My apologies for requiring you to repeat your answer from last month. I found your post not long after I posted.
From your example, and other examples that use the FAMILY class, the KIDS are instantiated at the same time as the FAMILY instance. In my case, the KIDS are instantiated at different times and then "connected" in the future. I have an attempt at the end of the email which is based off your example. It does not work, and I am not sure why.
I noticed in your examples that you use macros. Is there something significant about that?
Thanks Mark
From looking at your code there are a couple of points to be noted:
1. Use of macro c?
This is a macro. As such it is evaluated at macro-expansion time. Using it as a value to be bound to a slot value (as you do in #'make-controller) is not of any meaning - it will return a cell slot that is then assigned as a value to slot item-index in the view instance.
So: You cannot use c? at runtime as a value-returning form.
2. Assigning / creating cells or cell rules via c? and the likes
There are two points in time where these can be used: At macro expansion time and at instance creation time (when initargs are being evaluated). This is based on how Cells acts on slot definitions. The macros (!) defmodel and defmd (which is just a short version of defmodel) are examining the class definition when the reader seas the defmodel / defmd form.
If you look at the output of the observer then it is clear that the value of the slot item-index is not a "normal" value but a cell definition/cell instance.
This means: Assigning cell rules at execution time is possible but not via c?.
3. Use macros
Points 1 and 2 lead to the "style" I showed using macros to create means for making instances with cells-enabled slots. You may want to use macroexpansion to look at what defmodel creates. The macro defmodel is defined in file defmodel.lisp in the cells source code.
Note to other readers of this message: Yes, this is a simplified if not, strictly speaking, incomplete answer. I hope, though, that it makes clear some principles and provides enough guidance to overcome initial Cells hurdles.
4. Dependency pattern design
The Controller is not really a "bridge" between model and view. You try to implement a direct dependency between the model and view via
(setf (item-index view) (c? (let ((m (model (fm^ :root)))) (item-index m))))
in #'make-controller.
If you created a slot view-item-index and also a slot model-item-index in the controller class then you would, with a bit more effort, be able to express dependencies between those two new slots at controller level. In the view then you'd have to create a cell rule that forms a dependency on view-item-index in the controller. All in all a different pattern that would require a different sequence of instance creation: model -> controller -> view. The view then would have to register itself with its parent, the controller.
Hope this helps! (Have you looked at the examples provided by Kenny ?)
Frank
Sorry, gents. I should have scrolled down! More below:
On Wed, Oct 3, 2012 at 6:06 PM, Mark Cox markcox80@gmail.com wrote:
Hi Frank,
Thanks for your example. My apologies for requiring you to repeat your answer from last month. I found your post not long after I posted.
From your example, and other examples that use the FAMILY class, the KIDS are instantiated at the same time as the FAMILY instance. In my case, the KIDS are instantiated at different times and then "connected" in the future. I have an attempt at the end of the email which is based off your example. It does not work, and I am not sure why.
I noticed in your examples that you use macros. Is there something significant about that?
Thanks Mark
(defpackage "MODEL" (:use "COMMON-LISP" "CELLS")) (in-package "MODEL")
;; model
(defmodel my-model (model) ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defun make-model (id) (make-instance 'my-model :md-name id))
;; view
(defmodel my-view (model) ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defun make-view (id) (make-instance 'my-view :md-name id))
(defobserver item-index ((object my-view) new-value) (format t "~&VIEW item-index ~A~%" new-value))
;; controller
(defmodel my-controller (family) ((model :initarg :model :accessor model) (view :initarg :view :accessor view)))
(defun make-controller (model view) (setf (fm-parent model) :root (fm-parent view) :root) (let ((rv (make-instance 'my-controller :model (c-in model) :view (c-in view) :kids (c? (the-kids model view)) :md-name :root)))
(setf (item-index view) (c? (let ((m (model (fm^ :root)))) (item-index m)))) rv))
;; test
(defun test () (let* ((model (make-model :model)) (view (make-view :view)) (controller (make-controller model view))) (setf (item-index model) 2) (item-index view)))
If the above is really what you want to do you can just:
(defun test ()
(let* ((model (make-model :model)) (view (make-instance 'view
))
(controller (make-controller model view))) (setf (item-index model) 2) (item-index view)))
Sorry, Google decided to send that. Trying again:
(defun test () (let* ((model (make-model :model)) (view (make-view :view)) (controller (make-controller model view))) (setf (item-index model) 2) (item-index view)))
If the above is really what you want to do you can just:
(defun test () (let* ((model (make-model :model)) (view (make-instance 'view :item-index (c? (item-index model))) ;; model is captured in a closure via lexical scoping (setf (item-index model) 2) (item-index view)))
When you want to build things other ways, you just always need to be able to navigate around your application model to find the entities on which you want another entity to depend, and the solution will vary based on how your model changes shape over time (so ask again when you get to something more elaborate than the above).
hth, kt
ps. Hi, Frank!
On Tue, Oct 2, 2012 at 9:47 PM, Mark Cox markcox80@gmail.com wrote:
G'day,
I think my mental model of Cells is wrong. Perhaps someone can help.
I have two model classes:
(defmodel model () ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
(defmodel view () ((item-index :initarg :item-index :accessor item-index)) (:default-initargs :item-index (c-in 0)))
What I would like to do is to connect the item-index slot of an instance of MODEL to the item-index in an instance of VIEW. i.e.
(defun connect-model-and-view (model view) (setf (item-index view) (c? (item-index model))))
which does not appear to be possible.
Correct, that is not how Cells works (though it could be made to work if necessary).
Instead, the view would be instantiated after the model -- or with the model in one "datapulse" -- with model either supplied as an initarg or located by the view rule during model initialization:
Either:
(make-instance 'view :model <some-model-instance already extant> :item-index (c? (item-index (model self)))
Or (the way my models work)
(make-instance 'view :item-index (c? (item-index <navigate-app-model-finding-desired-specific-model>)))
The key then is having model objects organized in a way that one can find other entities dynamically as the larger model formed by all the model objects comes to life.
The family class and its supporting utilities are how I generally manage the Cells modelspace of an application.
hth, kt
I have navigated around the issue by introducing a third model class, CONTROLLER, that has an item-index slot initialised with (c? (item-index (^model))) and an observer that updates the view. However, I am wondering if this is the correct way as I seem to require inserting WITH-INTEGRITY in the observer.
Thanks Mark
cells-devel site list cells-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/cells-devel