[Openmcl-devel] Thread-local bindings
martin
brooks.martin at sympatico.ca
Fri Jun 14 18:18:52 PDT 2019
Thank you Ron, Rainer and Bill
I hadn’t thought about the pervasiveness of defvar !
Also, from CLtL2, sec 9.2: A special declaration does not affect bindings pervasively. Inner bindings of a variable implicitly shadow a special declaration and must be explicitly re-declared to be special. (However, a specialproclamation does pervasively affect bindings; this exception is made for reasons of convenience and compatibility with MacLisp.)
Let’s look at special variables.
The following result is expected:
? (process-run-function :process1
#'(lambda ()
(let ((foo 4))
(declare (special foo)) ; NOTE
(flet ((bar () (print foo) :return-me-from-bar))
(bar)
(process-run-function :process2 #'bar)
:return-me-from-lambda))))
4
> Error: Unbound variable: FOO
> While executing: BAR, in process PROCESS2(3).
The thread-local binding rule looks like:
— Lambda lexically closes over code running in a newly generated thread:
— except when a free variable in the new thread was dynamically scoped in the parent thread.
And … timing is everything!
When one wraps the test expression in a function definition, then it works one way when the definition is compiled before defvar declared, and it works the other way when the definition is compiled after defvar.
Actually, I am amazed that the closed-over case works.
In the following form, process1 terminates before process2 calls bar; nevertheless the call to bar has access to the lexical environment of process1 — or maybe that isn’t a correct description.
(process-run-function :process1
#'(lambda ()
(let ((foo 4))
(flet ((bar () (print foo) :return-me-from-bar))
(bar)
(process-run-function :process2 #'(lambda ()
(sleep 10)
(bar)
:return-me-from-lambda))))))
Martin
> On Jun 14, 2019, at 12:18 PM, Ron Garret <ron at flownet.com> wrote:
>
> You might want to read this:
>
> http://www.flownet.com/ron/specials.pdf <http://www.flownet.com/ron/specials.pdf>
>
> and particularly the section called “the pervasiveness of defvar”.
>
> Also: instead of running your code at the top level, try putting it inside a function and calling the function before and after the DEFVAR. You will see the different results. The thing that is surprising you is happening at compile time.
>
> rg
>
> On Jun 14, 2019, at 9:14 AM, Rainer Joswig <joswig at lisp.de <mailto:joswig at lisp.de>> wrote:
>
>> If a variable is used in a function and there is no special declaration and there is no DEFVAR, then the binding is lexical.
>>
>> DEFVAR declares a variable to be special.
>>
>> Then ALL new code on all levels (all lets, lambda vars, ...) will use dynamic binding.
>>
>>
>>
>>> Am 14.06.2019 um 16:16 schrieb martin <brooks.martin at sympatico.ca <mailto:brooks.martin at sympatico.ca>>:
>>>
>>> Hello All
>>>
>>> I am confused about the semantics of thread-local bindings when using process-run-function.
>>> CCL version: Clozure Common Lisp Version 1.12-dev (v1.12-dev.4-3-gdd5622e9) DarwinX8664
>>>
>>> Suppose that the variable foo is not globally bound:
>>>
>>> ? foo
>>> > Error: Unbound variable: FOO
>>>
>>> Consider the following form:
>>>
>>> (process-run-function :process1
>>> #'(lambda ()
>>> (let ((foo 2))
>>> (flet ((bar () (print foo) :return-me-from-foo))
>>> (bar)
>>> (process-run-function :process2 #'bar)
>>> :return-me-from-lambda))))
>>>
>>> The result is to print 2 twice:
>>>
>>> 2
>>> 2
>>>
>>> Neither keyword return value appears, nor should they — I put them in the code to disambiguate printing from the value returned by print.
>>>
>>> Now bind foo at top level.
>>>
>>> ? (defvar foo 3)
>>> FOO
>>>
>>> Execute the form again, to get these to print results:
>>>
>>> 2
>>> 3
>>>
>>> My confusion:
>>> The second result (print 2 & 3) demonstrates my understanding of thread-local bindings.
>>> The first result (print 2 & 2) seems wrong — why didn’t Lisp complain that foo was unbound in the call to bar within :process2 ?
>>
>> Because FOO uses the lexical binding introduced by the LET.
>>
>>>
>>> My confusion gets worse — restart Lisp, so that foo is not globally bound, and do the same tests with the following form, to see it print 4 & 4.
>>>
>>> (let ((foo 4))
>>> (process-run-function :process1
>>> #'(lambda ()
>>> (flet ((bar () (print foo) :return-me-from-foo))
>>> (bar)
>>> (process-run-function :process2 #'bar)
>>> :return-me-from-lambda))))
>>>
>>> 4
>>> 4
>>>
>>
>>
>> lexical binding
>>
>>
>>> And now globally bind foo:
>>>
>>> ? (defvar foo 5)
>>> FOO
>>
>>
>>>
>>> The form now prints 5 & 5 .
>>>
>>> 5
>>> 5
>>>
>>
>> dynamic binding
>>
>>
>>> Help! What’s the rule?!?
>>>
>>> Furthermore, I take it that stack-local bindings apply to variables only, not functions.
>>> To test this, I gave bar a global definition:
>>>
>>> (defun bar () (print 27))
>>>
>>> But this did not change the above results; in other words, the local definition was used in both threads.
>>>
>>> All consolation gratefully accepted,
>>> Martin
>>>
>>>
>>>
>>>
>>>
>>>
>>> _______________________________________________
>>> Openmcl-devel mailing list
>>> Openmcl-devel at clozure.com <mailto:Openmcl-devel at clozure.com>
>>> https://lists.clozure.com/mailman/listinfo/openmcl-devel <https://lists.clozure.com/mailman/listinfo/openmcl-devel>
>>
>> _______________________________________________
>> Openmcl-devel mailing list
>> Openmcl-devel at clozure.com <mailto:Openmcl-devel at clozure.com>
>> https://lists.clozure.com/mailman/listinfo/openmcl-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20190614/6e4ba769/attachment.htm>
More information about the Openmcl-devel
mailing list