dependency tracking not working properly?
Created by: reiniervandijk
Consider these two objects:
(define-object test ()
:computed-slots
((a 0)
(b (prog1 (the a)
(the (set-slot! :a (+ (the a) 1)))))))
(define-object test2 ()
:computed-slots
((a 0)
(b (prog1 (the a)
(the (set-slot! :c (+ (the a) 1)))))
(c 2)))
In the first case, the dependency of :b to :a is lost. In the latter case, it still works. This seems an issue or please explain why it isn't. I encountered this when I tried to make a computed-slot in the tasty inspector remain :unbound forever, as to imitate a function that runs on every click again and again.
> (make-self 'test)
#<test @ #x24d54b5a>
> (the a)
0
> (the b)
0
> (the a)
1
> (the (set-slot! :a 33))
nil
> (the (slot-status :b))
:evaluated
> (make-self 'test2)
#<test2 @ #x24d9c422>
> (the a)
0
> (the b)
0
> (the c)
1
> (the (set-slot! :a 33))
nil
> (the (slot-status :b))
:unbound
Imported comments:
By genworks on 2013-02-12 15:22:16 UTC
It is not, has never been, and probably never will be supported to call set-slot! from within the body of a cached computed-slot.
This issue will remain open until this "feature" is better documented or maybe even protected against by the compiler.
On Tue, Feb 12, 2013 at 10:03 AM, reiniervandijk notifications@github.comwrote:
Consider these two objects:
(define-object test () :computed-slots ((a 0) (b (prog1 (the a) (the (set-slot! :a (+ (the a) 1))))))) (define-object test2 () :computed-slots ((a 0) (b (prog1 (the a) (the (set-slot! :c (+ (the a) 1))))) (c 2)))
In the first case, the dependency to a is lost. In the latter case, it still works. This seems an issue or please explain why it isn't.
move> (make-self 'test)#<test @ #x24d54b5a>> (the a)0> (the b)0> (the a)1> (the (set-slot! :a 33))nil> (the (slot-status :b)):evaluatedmove> (make-self 'test2) #<test2 @ #x24d9c422>> (the a)0> (the b)0> (the c)1> (the (set-slot! :a 33))nil> (the (slot-status :b)):unbound
— Reply to this email directly or view it on GitHubhttps://github.com/genworks/Genworks-GDL/issues/75.
-- My Best,
Dave Cooper, Genworks Support david.cooper@genworks.com, dave.genworks.com(skype) USA: 248-327-3253(o), 1-248-330-2979(mobile) UK: 0191 645 1699
By reiniervandijk on 2013-02-12 15:40:58 UTC
This answer is clear, but not really addressing the issue pointed out here. Then please consider
(define-object test3 ()
:functions
((foo
()
(the (set-slot! :a (+ (the a) 1)))))
:computed-slots
((a 0)
(b (prog1 (the a)
(the foo)))))
and try the same. If I read you correctly, a function is allowed to set-slot! something. I know we use it often here.
By genworks on 2013-02-12 16:04:49 UTC
Let's be more general here: it is not supported to do side-effecting within the body of a cached computed-slot.
The above form implies a side-effect by calling (the foo), which is a side-effecting function (because itself calls set-slot!), within the body of the computed slot.
One practical effect of this is that it never makes sense to use progn, prog1, etc in the body of a computed-slot. The computed slot is intended to contain exactly one expression, and only to use the return-value of that expression, and without side-effects. Although in the above example, even without the prog1, if it were just
...
:computed-slots
((a 0)
(b (the foo)))
the principle would be violated because (the foo) is a side-effecting function being called from within the body of a computed-slot.
On Tue, Feb 12, 2013 at 10:40 AM, reiniervandijk notifications@github.comwrote:
This answer is clear, but not really addressing the issue pointed out here. Then please consider
(define-object test3 ()
:functions ((foo () (the (set-slot! :a (+ (the a) 1)))))
:computed-slots ((a 0) (b (prog1 (the a) (the foo)))))
and try the same. If I read you correctly, a function is allowed to set-slot! something. I know we use it often here.
— Reply to this email directly or view it on GitHubhttps://github.com/genworks/Genworks-GDL/issues/75#issuecomment-13438267.
-- My Best,
Dave Cooper, Genworks Support david.cooper@genworks.com, dave.genworks.com(skype) USA: 248-327-3253(o), 1-248-330-2979(mobile) UK: 0191 645 1699
By reiniervandijk on 2013-02-12 16:32:48 UTC
Alright. Do you feel this (rather important) aspect is clearly documented somewhere? It basically defines the main paradigm behind programming slots.
By genworks on 2013-02-12 21:12:02 UTC
It definitely needs to be documented in the new tutorial.pdf.
Moreover, your second example (with the indirect call to set-slot!) could never be picked up at compile-time. But, it will be possible to add a low-cost runtime check which will throw an error if an operation like this is attempted.
This Issue will remain open until the documentation is made clear, and the runtime check is in place.
On Feb 12, 2013, at 9:32 AM, reiniervandijk notifications@github.com wrote:
Alright. Do you feel this (rather important) aspect is clearly documented somewhere? It basically defines the main paradigm behind programming slots.
— Reply to this email directly or view it on GitHub.
By genworks on 2013-02-18 19:46:03 UTC
The development version of Gendl now throws a runtime Warning if a set-slot! is attempted anywhere (dynamically) from within the body of a cached slot.
Any opinion on whether this should be an Error instead of a Warning? Or it could be configurable between Error and Warning using a parameter such as cached-side-effecting-behavior (possible values of :warn, :error, or nil).
By reiniervandijk on 2013-02-18 21:40:05 UTC
The latter option would be best. Default to :error perhaps (because it's not supported) and give error message with cause and solution approach. Maybe also a explanation of how to set this setting in gdlinit. Are you not also using side-effecting in GWL base-ajax sheet, to register sheet-sections? In that case don't forget to locally bind parameter value to nil...
Verstuurd vanaf mijn iPad
Op 18 feb. 2013 om 20:46 heeft Dave Cooper notifications@github.com het volgende geschreven:
The development version of Gendl now throws a runtime Warning if a set-slot! is attempted anywhere (dynamically) from within the body of a cached slot.
Any opinion on whether this should be an Error instead of a Warning? Or it could be configurable between Error and Warning using a parameter such as cached-side-effecting-behavior (possible values of :warn, :error, or nil).
— Reply to this email directly or view it on GitHub.
By genworks on 2013-04-11 23:15:57 UTC
In current codebase and currently released Genworks-GDL 1582 release candidate, this is Warning and not an Error. We have started receiving feedback from the field on it. For safety sake we will consider changing the default to be an Error, after most existing codebase have had a chance to be audited for the Warnings. The issue remains open until it is documented.
By genworks on 2013-04-11 23:17:32 UTC
The current version also allows for a keyword argument for set-slot of :warn-on-non-toplevel?
This defaults to t but if you have a case which you know is benign, you can do
(the ... (set-slot! ... :warn-on-non-toplevel? nil))
By genworks on 2013-04-11 23:20:22 UTC
One case where this is probably benign is when the bashee is in a separate tree (e.g. constructed with a raw make-object rather than as child :object). The runtime machinery can probably be made to detect these cases and automatically suppress the warning. But for the time being it will be necessary to use the :warn-on-non-toplevel? nil keyword argument in order to suppress warnings in these cases.
Note that the :warn-on-non-toplevel? keyword argument is not documented and subject to change, so any use of it in production code should be flagged.