* Self-evaluating function and closure @ 2019-06-12 20:29 Vladimir Zhbanov 2019-06-15 19:35 ` John Cowan ` (3 more replies) 0 siblings, 4 replies; 14+ messages in thread From: Vladimir Zhbanov @ 2019-06-12 20:29 UTC (permalink / raw) To: guile-user Greetings, I have tried almost a textbook example with Guile 2.2.4: scheme@(guile-user)> (define (function-generator) (let ((func #f)) (lambda () (set! func (let a () a)) func))) scheme@(guile-user)> (define x (function-generator)) scheme@(guile-user)> (define y (function-generator)) scheme@(guile-user)> x $20 = #<procedure f9f9d0 at <unknown port>:562:25 ()> scheme@(guile-user)> y $21 = #<procedure bf2660 at <unknown port>:562:25 ()> scheme@(guile-user)> (x) $22 = #<procedure a ()> scheme@(guile-user)> (y) $23 = #<procedure a ()> scheme@(guile-user)> (eq? (x) (y)) $24 = #t The result is unexpected for me, I expected a new self-evaluating procedure every time I run the function-generator procedure (and it works differently with Guile 2.0, IIUC, cannot check just now). AFAICS, Guile creates a toplevel procedure "a" while it should not do so. scheme@(guile-user)> a $25 = #<procedure 109aa90 at <unknown port>:422:25 ()> Questions: - Is this a bug? - Is there a way to work around this (either using the above 'let' construct or anything else)? Thanks in advance -- Vladimir (λ)επτόν EDA — https://github.com/lepton-eda ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-12 20:29 Self-evaluating function and closure Vladimir Zhbanov @ 2019-06-15 19:35 ` John Cowan 2019-06-16 0:39 ` Mark H Weaver 2019-06-16 0:36 ` Mark H Weaver ` (2 subsequent siblings) 3 siblings, 1 reply; 14+ messages in thread From: John Cowan @ 2019-06-15 19:35 UTC (permalink / raw) To: guile-user On Sat, Jun 15, 2019 at 4:31 AM Vladimir Zhbanov <vzhbanov@gmail.com> wrote: The result is unexpected for me, I expected a new self-evaluating > procedure every time I run the function-generator procedure (and > it works differently with Guile 2.0, IIUC, cannot check just now). > I tested this on Chicken, Biwa, and Chbi, and all agree on producing new closures that are not eq?. So I'd call it a bug in either Guile or the Scheme standard, but probably in Guile. > AFAICS, Guile creates a toplevel procedure "a" while it should not > do so. > I am not able to reproduce this on Guile 2.2.4. Try restarting Guile and doing just the commands above and see if it persists. John Cowan http://vrici.lojban.org/~cowan cowan@ccil.org The whole of Gaul is quartered into three halves. --Julius Caesar ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-15 19:35 ` John Cowan @ 2019-06-16 0:39 ` Mark H Weaver 0 siblings, 0 replies; 14+ messages in thread From: Mark H Weaver @ 2019-06-16 0:39 UTC (permalink / raw) To: John Cowan; +Cc: guile-user Hi John, John Cowan <cowan@ccil.org> writes: > On Sat, Jun 15, 2019 at 4:31 AM Vladimir Zhbanov <vzhbanov@gmail.com> wrote: > > The result is unexpected for me, I expected a new self-evaluating >> procedure every time I run the function-generator procedure (and >> it works differently with Guile 2.0, IIUC, cannot check just now). >> > > I tested this on Chicken, Biwa, and Chbi, and all agree on producing new > closures that are not eq?. So I'd call it a bug in either Guile or > the Scheme standard, but probably in Guile. I don't understand your reasoning. If you think it's a bug in Guile, can you cite a specific requirement in a relevant Scheme standard that we are violating here? I also don't know why you would call it a bug in the Scheme standard. Can you explain why you think this? Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-12 20:29 Self-evaluating function and closure Vladimir Zhbanov 2019-06-15 19:35 ` John Cowan @ 2019-06-16 0:36 ` Mark H Weaver 2019-06-16 9:02 ` Vladimir Zhbanov 2019-06-16 9:47 ` Mark H Weaver 2019-06-16 11:29 ` Mark H Weaver 3 siblings, 1 reply; 14+ messages in thread From: Mark H Weaver @ 2019-06-16 0:36 UTC (permalink / raw) To: guile-user Hi Vladimir, Vladimir Zhbanov <vzhbanov@gmail.com> writes: > Greetings, > > I have tried almost a textbook example with Guile 2.2.4: > > scheme@(guile-user)> (define (function-generator) > (let ((func #f)) > (lambda () (set! func (let a () a)) func))) > > scheme@(guile-user)> (define x (function-generator)) > scheme@(guile-user)> (define y (function-generator)) > scheme@(guile-user)> x > $20 = #<procedure f9f9d0 at <unknown port>:562:25 ()> > scheme@(guile-user)> y > $21 = #<procedure bf2660 at <unknown port>:562:25 ()> > scheme@(guile-user)> (x) > $22 = #<procedure a ()> > scheme@(guile-user)> (y) > $23 = #<procedure a ()> > scheme@(guile-user)> (eq? (x) (y)) > $24 = #t > > The result is unexpected for me, I expected a new self-evaluating > procedure every time I run the function-generator procedure (and > it works differently with Guile 2.0, IIUC, cannot check just now). Why would you expect 'eq?' to return #false here? Do you know of any text in Guile's manual, or in any of the relevant Scheme standards, that would lead you to expect this? Since (let a () a) contains no free variable references, every procedure returned by (let a () a) is operationally equivalent to every other procedure returned by it. Therefore, as I understand it, a conforming Scheme implementation is permitted (but not required) to return the same procedure object every time. I just refreshed my memory of the requirements of the R5RS, R6RS, and R7RS on 'eq?' when applied to procedures. Conforming implementations are required to return #true if the procedures have the same "location tags", and are required to return #false if the procedures would behave differently (return different value(s) or have different side effects) for some arguments. > AFAICS, Guile creates a toplevel procedure "a" while it should not do >so. > > scheme@(guile-user)> a > $25 = #<procedure 109aa90 at <unknown port>:422:25 ()> If this were the case, it would certainly be a bug. However, I cannot reproduce it, and I strongly suspect that you had defined 'a' as a toplevel variable earlier in your Guile session and forgot about it. Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 0:36 ` Mark H Weaver @ 2019-06-16 9:02 ` Vladimir Zhbanov 2019-06-16 10:09 ` Mark H Weaver 0 siblings, 1 reply; 14+ messages in thread From: Vladimir Zhbanov @ 2019-06-16 9:02 UTC (permalink / raw) To: guile-user Mark, On Sat, Jun 15, 2019 at 08:36:34PM -0400, Mark H Weaver wrote: > Hi Vladimir, > > Vladimir Zhbanov <vzhbanov@gmail.com> writes: > > > Greetings, > > > > I have tried almost a textbook example with Guile 2.2.4: > > > > scheme@(guile-user)> (define (function-generator) > > (let ((func #f)) > > (lambda () (set! func (let a () a)) func))) > > > > scheme@(guile-user)> (define x (function-generator)) > > scheme@(guile-user)> (define y (function-generator)) > > scheme@(guile-user)> x > > $20 = #<procedure f9f9d0 at <unknown port>:562:25 ()> > > scheme@(guile-user)> y > > $21 = #<procedure bf2660 at <unknown port>:562:25 ()> > > scheme@(guile-user)> (x) > > $22 = #<procedure a ()> > > scheme@(guile-user)> (y) > > $23 = #<procedure a ()> > > scheme@(guile-user)> (eq? (x) (y)) > > $24 = #t > > > > The result is unexpected for me, I expected a new self-evaluating > > procedure every time I run the function-generator procedure (and > > it works differently with Guile 2.0, IIUC, cannot check just now). > > Why would you expect 'eq?' to return #false here? Do you know of any > text in Guile's manual, or in any of the relevant Scheme standards, that > would lead you to expect this? > > Since (let a () a) contains no free variable references, every procedure > returned by (let a () a) is operationally equivalent to every other > procedure returned by it. Therefore, as I understand it, a conforming > Scheme implementation is permitted (but not required) to return the same > procedure object every time. If the procedure is defined inside a closure, should it be available at toplevel (especially with the same name it was defined inside a function)? > > I just refreshed my memory of the requirements of the R5RS, R6RS, and > R7RS on 'eq?' when applied to procedures. Conforming implementations > are required to return #true if the procedures have the same "location > tags", and are required to return #false if the procedures would behave > differently (return different value(s) or have different side effects) > for some arguments. > > > AFAICS, Guile creates a toplevel procedure "a" while it should not do > >so. > > > > scheme@(guile-user)> a > > $25 = #<procedure 109aa90 at <unknown port>:422:25 ()> > > If this were the case, it would certainly be a bug. However, I cannot > reproduce it, and I strongly suspect that you had defined 'a' as a > toplevel variable earlier in your Guile session and forgot about it. No, I'haven't defined anything before. OK, let's start with a fresh session and use another name: <Geiser session starts here> GNU Guile 2.2.4 Copyright (C) 1995-2017 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> (define (function-generator) (let ((func #f)) (lambda () (set! func (let b () b)) func))) scheme@(guile-user)> (define x (function-generator)) scheme@(guile-user)> (define y (function-generator)) scheme@(guile-user)> x $4 = #<procedure 16949b0 at <unknown port>:108:25 ()> scheme@(guile-user)> y $5 = #<procedure 1860200 at <unknown port>:108:25 ()> scheme@(guile-user)> (x) $6 = #<procedure b ()> scheme@(guile-user)> (y) $7 = #<procedure b ()> scheme@(guile-user)> (eq? (x) (y)) $8 = #t scheme@(guile-user)> ,compile (define (function-generator) (let ((func #f)) (lambda () (set! func (let b () b)) func))) Disassembly of <unnamed function> at #x98: 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):185:9 1 (static-ref 1 75) ;; function-generator 3 (define! 1 1) 4 (make-non-immediate 0 74) ;; #<procedure function-generator ()> 6 (box-set! 1 0) 7 (make-short-immediate 0 2052) ;; #<unspecified> 8 (handle-interrupts) 9 (return-values 2) ;; 1 value Disassembly of function-generator at #xc0: 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):185:9 1 (make-short-immediate 1 4) ;; #f 2 (box 1 1) 3 (make-closure 0 7 1) ;; anonymous procedure at #xb2c0aac8 (1 free var) at (unknown file):187:25 6 (free-set! 0 1 0) ;; free var 0 8 (handle-interrupts) 9 (return-values 2) ;; 1 value Disassembly of <unnamed function> at #xe8: 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):187:25 1 (free-ref 1 1 0) ;; free var 0 3 (make-non-immediate 0 57) ;; #<procedure b ()> 5 (box-set! 1 0) at (unknown file):187:36 6 (make-non-immediate 0 54) ;; #<procedure b ()> 8 (handle-interrupts) 9 (return-values 2) ;; 1 value Disassembly of b at #x110: 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):187:47 1 (make-non-immediate 0 49) ;; #<procedure b ()> 3 (handle-interrupts) 4 (return-values 2) ;; 1 value Disassembly of <unnamed function> at #x124: 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):187:47 1 (static-patch! 37 16) 4 (make-non-immediate 1 33) ;; "function-generator" 6 (string->symbol 1 1) 7 (static-set! 1 34) ;; function-generator 9 (static-patch! 35 -34) 12 (static-patch! 34 -17) 15 (make-short-immediate 0 2052) ;; #<unspecified> 16 (return-values 2) ;; 1 value <Geiser session ends here> The same behaviour, though now the procedure name is 'b'. The line that confuses me here is: 5 (box-set! 1 0) at (unknown file):187:36 I suspect this is a toplevel definition. To make it clear, why I ask here, this new behaviour in Guile 2.2 broke our schematic frontend actions (that were defined in such a way I presented above) after moving from Guile 2.0 to it. IIUC, at least guile versions compiled on Debian and FreeBSD involved. -- Vladimir (λ)επτόν EDA — https://github.com/lepton-eda ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 9:02 ` Vladimir Zhbanov @ 2019-06-16 10:09 ` Mark H Weaver 2019-06-17 8:03 ` Vladimir Zhbanov 0 siblings, 1 reply; 14+ messages in thread From: Mark H Weaver @ 2019-06-16 10:09 UTC (permalink / raw) To: guile-user Hi Vladimir, Vladimir Zhbanov <vzhbanov@gmail.com> writes: > On Sat, Jun 15, 2019 at 08:36:34PM -0400, Mark H Weaver wrote: >> Vladimir Zhbanov <vzhbanov@gmail.com> writes: >> >> > I have tried almost a textbook example with Guile 2.2.4: >> > >> > scheme@(guile-user)> (define (function-generator) >> > (let ((func #f)) >> > (lambda () (set! func (let a () a)) func))) >> > >> > scheme@(guile-user)> (define x (function-generator)) >> > scheme@(guile-user)> (define y (function-generator)) >> > scheme@(guile-user)> x >> > $20 = #<procedure f9f9d0 at <unknown port>:562:25 ()> >> > scheme@(guile-user)> y >> > $21 = #<procedure bf2660 at <unknown port>:562:25 ()> >> > scheme@(guile-user)> (x) >> > $22 = #<procedure a ()> >> > scheme@(guile-user)> (y) >> > $23 = #<procedure a ()> >> > scheme@(guile-user)> (eq? (x) (y)) >> > $24 = #t >> > >> > The result is unexpected for me, I expected a new self-evaluating >> > procedure every time I run the function-generator procedure (and >> > it works differently with Guile 2.0, IIUC, cannot check just now). >> >> Why would you expect 'eq?' to return #false here? Do you know of any >> text in Guile's manual, or in any of the relevant Scheme standards, that >> would lead you to expect this? >> >> Since (let a () a) contains no free variable references, every procedure >> returned by (let a () a) is operationally equivalent to every other >> procedure returned by it. Therefore, as I understand it, a conforming >> Scheme implementation is permitted (but not required) to return the same >> procedure object every time. > > If the procedure is defined inside a closure, should it be > available at toplevel (especially with the same name it was > defined inside a function)? A variable bound within a local lexical environment certainly should not be visible at toplevel. In particular, the transcript that you provided above should *not* result in 'a' being bound in the toplevel environment. When I type those precise commands into Guile 2.2.4 on my system, 'a' is *not* bound at toplevel. Here's what I see: mhw@jojen ~$ guile GNU Guile 2.2.4 Copyright (C) 1995-2017 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> (define (function-generator) (let ((func #f)) (lambda () (set! func (let a () a)) func))) scheme@(guile-user)> (define x (function-generator)) scheme@(guile-user)> (define y (function-generator)) scheme@(guile-user)> x $1 = #<procedure e93ea0 at <unknown port>:3:26 ()> scheme@(guile-user)> y $2 = #<procedure fb8660 at <unknown port>:3:26 ()> scheme@(guile-user)> (x) $3 = #<procedure a ()> scheme@(guile-user)> (y) $4 = #<procedure a ()> scheme@(guile-user)> (eq? (x) (y)) $5 = #t scheme@(guile-user)> a ;;; <unknown-location>: warning: possibly unbound variable `a' ERROR: In procedure module-lookup: Unbound variable: a Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. scheme@(guile-user) [1]> >> If this were the case, it would certainly be a bug. However, I cannot >> reproduce it, and I strongly suspect that you had defined 'a' as a >> toplevel variable earlier in your Guile session and forgot about it. > > No, I'haven't defined anything before. > > OK, let's start with a fresh session and use another name: > > <Geiser session starts here> > > GNU Guile 2.2.4 > Copyright (C) 1995-2017 Free Software Foundation, Inc. > > Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. > This program is free software, and you are welcome to redistribute it > under certain conditions; type `,show c' for details. > > Enter `,help' for help. > scheme@(guile-user)> (define (function-generator) > (let ((func #f)) > (lambda () (set! func (let b () b)) func))) > scheme@(guile-user)> (define x (function-generator)) > scheme@(guile-user)> (define y (function-generator)) > scheme@(guile-user)> x > $4 = #<procedure 16949b0 at <unknown port>:108:25 ()> > scheme@(guile-user)> y > $5 = #<procedure 1860200 at <unknown port>:108:25 ()> > scheme@(guile-user)> (x) > $6 = #<procedure b ()> > scheme@(guile-user)> (y) > $7 = #<procedure b ()> > scheme@(guile-user)> (eq? (x) (y)) > $8 = #t This new transcript does not include an evaluation of 'b' at toplevel. Can you show me a complete transcript that demonstrates this behavior? > scheme@(guile-user)> ,compile (define (function-generator) > (let ((func #f)) > (lambda () (set! func (let b () b)) func))) > Disassembly of <unnamed function> at #x98: > > 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):185:9 > 1 (static-ref 1 75) ;; function-generator > 3 (define! 1 1) > 4 (make-non-immediate 0 74) ;; #<procedure function-generator ()> > 6 (box-set! 1 0) > 7 (make-short-immediate 0 2052) ;; #<unspecified> > 8 (handle-interrupts) > 9 (return-values 2) ;; 1 value > > > Disassembly of function-generator at #xc0: > > 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):185:9 > 1 (make-short-immediate 1 4) ;; #f > 2 (box 1 1) > 3 (make-closure 0 7 1) ;; anonymous procedure at #xb2c0aac8 (1 free var) at (unknown file):187:25 > 6 (free-set! 0 1 0) ;; free var 0 > 8 (handle-interrupts) > 9 (return-values 2) ;; 1 value > > > Disassembly of <unnamed function> at #xe8: > > 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):187:25 > 1 (free-ref 1 1 0) ;; free var 0 > 3 (make-non-immediate 0 57) ;; #<procedure b ()> > 5 (box-set! 1 0) at (unknown file):187:36 > 6 (make-non-immediate 0 54) ;; #<procedure b ()> > 8 (handle-interrupts) > 9 (return-values 2) ;; 1 value > > > Disassembly of b at #x110: > > 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):187:47 > 1 (make-non-immediate 0 49) ;; #<procedure b ()> > 3 (handle-interrupts) > 4 (return-values 2) ;; 1 value > > > Disassembly of <unnamed function> at #x124: > > 0 (assert-nargs-ee/locals 1 1) ;; 2 slots (0 args) at (unknown file):187:47 > 1 (static-patch! 37 16) > 4 (make-non-immediate 1 33) ;; "function-generator" > 6 (string->symbol 1 1) > 7 (static-set! 1 34) ;; function-generator > 9 (static-patch! 35 -34) > 12 (static-patch! 34 -17) > 15 (make-short-immediate 0 2052) ;; #<unspecified> > 16 (return-values 2) ;; 1 value > > <Geiser session ends here> > > The same behaviour, though now the procedure name is 'b'. > The line that confuses me here is: > > 5 (box-set! 1 0) at (unknown file):187:36 > > I suspect this is a toplevel definition. No, this is the (set! func ...) > To make it clear, why I ask here, this new behaviour in Guile 2.2 > broke our schematic frontend actions (that were defined in such a > way I presented above) after moving from Guile 2.0 to it. IIUC, > at least guile versions compiled on Debian and FreeBSD involved. I'm sorry about that, but if I understand correctly, it seems that lepton-eda is relying on unspecified behavior here. For better or worse, Scheme behavior is not fully specified in all cases. Comparing procedures for equivalence is one such case. The problem is undecidable in general, so the best we can hope for is an approximation, and we reserve the right to improve that approximation over time. I think it would be a mistake to prohibit such improvements. Regards, Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 10:09 ` Mark H Weaver @ 2019-06-17 8:03 ` Vladimir Zhbanov 0 siblings, 0 replies; 14+ messages in thread From: Vladimir Zhbanov @ 2019-06-17 8:03 UTC (permalink / raw) To: guile-user Hi Mark, John, My fault, sorry. In a fresh session there is no toplevel definition of the function defined by 'let'. Looking through Geiser history I found "(define a $9)". Probably this was the culprit. My apologies and thank you for your help! -- Vladimir (λ)επτόν EDA — https://github.com/lepton-eda ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-12 20:29 Self-evaluating function and closure Vladimir Zhbanov 2019-06-15 19:35 ` John Cowan 2019-06-16 0:36 ` Mark H Weaver @ 2019-06-16 9:47 ` Mark H Weaver 2019-06-16 10:21 ` Thomas Morley 2019-06-16 11:29 ` Mark H Weaver 3 siblings, 1 reply; 14+ messages in thread From: Mark H Weaver @ 2019-06-16 9:47 UTC (permalink / raw) To: guile-user Hello again Vladimir, Vladimir Zhbanov <vzhbanov@gmail.com> writes: > - Is there a way to work around this (either using the above 'let' > construct or anything else)? I'm not quite sure how to answer this question because I don't know what your requirements are. If you need to generate unique tags, any mutable object will do, e.g. a vector, list or string with at least one element that is not a literal. For example, (list #f), (vector #f), and (string #\a) must allocate a fresh object every time, but (list), (vector), (string), '(#f), #(#f), and "foo" may return the same object every time. If you need to generate a unique _procedure_, there's only one future-proof way to do it: the new procedure must behave differently than every other procedure, for some input. It's a mistake to expect procedures with equivalent behavior to be distinguishable in Scheme. If you feel that you need this, I would like to understand why. Note that 'eq?' is the same as 'eqv?' when applied to procedures (and most other types), and that Scheme 'eqv?' historically implements an approximation of "operational equivalence". That language was explicit in the R3RS, which defined 'eqv?' this way: The eqv? procedure implements an approximation to the relation of operational equivalence. It returns #t if it can prove that obj1 and obj2 are operationally equivalent. If it can't, it always errs on the conservative side and returns #f. with "operational equivalence" defined as follows: Two objects are operationally equivalent if and only if there is no way that they can be distinguished, using Scheme primitives other than eqv? or eq? or those like memq and assv whose meaning is defined explicitly in terms of eqv? or eq?. It is guaranteed that objects maintain their operational identity despite being named by variables or fetched from or stored into data structures. More recent Scheme standards have dropped this language, because the Scheme authors were not entirely satisfied with this definition and were unable to formulate a better one, but nonetheless "operational equivalence" remains the closest thing I've seen to a unifying principle of the meaning of Scheme 'eqv?'. Regards, Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 9:47 ` Mark H Weaver @ 2019-06-16 10:21 ` Thomas Morley 2019-06-16 10:32 ` Mark H Weaver 0 siblings, 1 reply; 14+ messages in thread From: Thomas Morley @ 2019-06-16 10:21 UTC (permalink / raw) To: Mark H Weaver; +Cc: guile-user [-- Attachment #1: Type: text/plain, Size: 3496 bytes --] Am So., 16. Juni 2019 um 11:49 Uhr schrieb Mark H Weaver <mhw@netris.org>: > > Hello again Vladimir, > > Vladimir Zhbanov <vzhbanov@gmail.com> writes: > > - Is there a way to work around this (either using the above 'let' > > construct or anything else)? > > I'm not quite sure how to answer this question because I don't know what > your requirements are. > > If you need to generate unique tags, any mutable object will do, e.g. a > vector, list or string with at least one element that is not a literal. > For example, (list #f), (vector #f), and (string #\a) must allocate a > fresh object every time, but (list), (vector), (string), '(#f), #(#f), > and "foo" may return the same object every time. > > If you need to generate a unique _procedure_, there's only one > future-proof way to do it: the new procedure must behave differently > than every other procedure, for some input. > > It's a mistake to expect procedures with equivalent behavior to be > distinguishable in Scheme. If you feel that you need this, I would like > to understand why. > > Note that 'eq?' is the same as 'eqv?' when applied to procedures (and > most other types), and that Scheme 'eqv?' historically implements an > approximation of "operational equivalence". That language was explicit > in the R3RS, which defined 'eqv?' this way: > > The eqv? procedure implements an approximation to the relation of > operational equivalence. It returns #t if it can prove that obj1 and > obj2 are operationally equivalent. If it can't, it always errs on the > conservative side and returns #f. > > with "operational equivalence" defined as follows: > > Two objects are operationally equivalent if and only if there is no > way that they can be distinguished, using Scheme primitives other than > eqv? or eq? or those like memq and assv whose meaning is defined > explicitly in terms of eqv? or eq?. It is guaranteed that objects > maintain their operational identity despite being named by variables > or fetched from or stored into data structures. > > More recent Scheme standards have dropped this language, because the > Scheme authors were not entirely satisfied with this definition and were > unable to formulate a better one, but nonetheless "operational > equivalence" remains the closest thing I've seen to a unifying principle > of the meaning of Scheme 'eqv?'. > > Regards, > Mark > Hi Mark, always interested in guile developments with regard to lilypond I noticed some inconsistence with my local lilypond-using-guile-2.9.2 installation. I think I could break it down to pure guile (no lilypond) (1) The already stated behaviour: ~$ guile GNU Guile 2.9.2 Copyright (C) 1995-2019 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> (define (function-generator) (let ((func #f)) (lambda () (set! func (let a () a)) func))) scheme@(guile-user)> (define x (function-generator)) scheme@(guile-user)> (define y (function-generator)) scheme@(guile-user)> (write (version)) "2.9.2" scheme@(guile-user)> (format #t "\nTEST: ~a" (eq? (x) (y))) TEST: #t $1 = #t (2) Doing it in a .scm-file (attached) ~$ guile --no-auto-compile eq-tst.scm "2.9.2" TEST: #f Did I something wrong or is it a bug? Cheers, Harm [-- Attachment #2: eq-tst.scm --] [-- Type: text/x-scheme, Size: 260 bytes --] (define (function-generator) (let ((func #f)) (lambda () (set! func (let a () a)) func))) (define x (function-generator)) (define y (function-generator)) (write (version)) (format #t "\nTEST: ~a" (eq? (x) (y))) ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 10:21 ` Thomas Morley @ 2019-06-16 10:32 ` Mark H Weaver 2019-06-16 10:42 ` Thomas Morley 0 siblings, 1 reply; 14+ messages in thread From: Mark H Weaver @ 2019-06-16 10:32 UTC (permalink / raw) To: Thomas Morley; +Cc: guile-user Hi Thomas, Thomas Morley <thomasmorley65@gmail.com> writes: > always interested in guile developments with regard to lilypond I > noticed some inconsistence with my local lilypond-using-guile-2.9.2 > installation. > I think I could break it down to pure guile (no lilypond) > > (1) The already stated behaviour: > ~$ guile > GNU Guile 2.9.2 > Copyright (C) 1995-2019 Free Software Foundation, Inc. > > Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. > This program is free software, and you are welcome to redistribute it > under certain conditions; type `,show c' for details. > > Enter `,help' for help. > scheme@(guile-user)> (define (function-generator) > (let ((func #f)) > (lambda () (set! func (let a () a)) func))) > scheme@(guile-user)> (define x (function-generator)) > scheme@(guile-user)> (define y (function-generator)) > scheme@(guile-user)> (write (version)) > "2.9.2" > scheme@(guile-user)> (format #t "\nTEST: ~a" (eq? (x) (y))) > > TEST: #t > $1 = #t > > (2) Doing it in a .scm-file (attached) > ~$ guile --no-auto-compile eq-tst.scm > "2.9.2" > TEST: #f > > Did I something wrong or is it a bug? Neither. This is a case of unspecified behavior, and the behavior of our compiler differs from that of our interpreter. You will notice similar discrepancies when comparing two literal lists or strings, where our compiler will aggressively unify literals within a compilation unit, and our interpreter does not: --8<---------------cut here---------------start------------->8--- GNU Guile 2.2.4 Copyright (C) 1995-2017 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> (eqv? "hello" "hello") $1 = #t scheme@(guile-user)> ,o interp #t scheme@(guile-user)> (eqv? "hello" "hello") $2 = #f scheme@(guile-user)> --8<---------------cut here---------------end--------------->8--- Regards, Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 10:32 ` Mark H Weaver @ 2019-06-16 10:42 ` Thomas Morley 0 siblings, 0 replies; 14+ messages in thread From: Thomas Morley @ 2019-06-16 10:42 UTC (permalink / raw) To: Mark H Weaver; +Cc: guile-user Hi Mark, Am So., 16. Juni 2019 um 12:35 Uhr schrieb Mark H Weaver <mhw@netris.org>: > > Did I something wrong or is it a bug? > > Neither. This is a case of unspecified behavior, and the behavior of > our compiler differs from that of our interpreter. You will notice > similar discrepancies when comparing two literal lists or strings, where > our compiler will aggressively unify literals within a compilation unit, > and our interpreter does not: [...] ah, ok, thanks for the explanation. Cheers, Harm ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-12 20:29 Self-evaluating function and closure Vladimir Zhbanov ` (2 preceding siblings ...) 2019-06-16 9:47 ` Mark H Weaver @ 2019-06-16 11:29 ` Mark H Weaver 2019-06-17 8:48 ` Vladimir Zhbanov 3 siblings, 1 reply; 14+ messages in thread From: Mark H Weaver @ 2019-06-16 11:29 UTC (permalink / raw) To: guile-user Hello again, Vladimir Zhbanov <vzhbanov@gmail.com> writes: > scheme@(guile-user)> (define (function-generator) > (let ((func #f)) > (lambda () (set! func (let a () a)) func))) [...] > - Is there a way to work around this (either using the above 'let' > construct or anything else)? Ideally, the code would be reworked to not expect equivalent procedures to be distinguishable. However, I should probably offer a hacky but expedient workaround. Here's one way to make otherwise equivalent procedures distinguishable: Allocate a fresh tag using (list #f), and arrange for the procedure to return that tag if it's called with a special input that's outside of the normal domain. Note that for Scheme procedures, the "input" is in general a list of arguments of arbitrary length. You could use 'case-lambda', which creates procedures that evaluate different body expressions depending on how many arguments are passed to it. Just add a case for an arity that you will never use, which returns the unique tag. In the example you gave, (let a () a) is equivalent to: ((letrec ((a (lambda () a))) a)) The procedure returned by (let a () a) expects 0 arguments. It will raise an error otherwise. We can repurpose the previously erroneous arity-1 case to return the unique tag, as follows: (let ((unique-tag (list #f))) ((letrec ((a (case-lambda (() a) ((x) unique-tag)))) a))) Every time the above expression is evaluated, it will necessarily return a unique procedure, which, if passed 0 arguments, behaves the same as the procedure returned by (let a () a). Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-16 11:29 ` Mark H Weaver @ 2019-06-17 8:48 ` Vladimir Zhbanov 2019-06-17 14:21 ` Mark H Weaver 0 siblings, 1 reply; 14+ messages in thread From: Vladimir Zhbanov @ 2019-06-17 8:48 UTC (permalink / raw) To: guile-user On Sun, Jun 16, 2019 at 07:29:59AM -0400, Mark H Weaver wrote: > Hello again, > > Vladimir Zhbanov <vzhbanov@gmail.com> writes: > > > scheme@(guile-user)> (define (function-generator) > > (let ((func #f)) > > (lambda () (set! func (let a () a)) func))) > > [...] > > > - Is there a way to work around this (either using the above 'let' > > construct or anything else)? > > Ideally, the code would be reworked to not expect equivalent procedures > to be distinguishable. However, I should probably offer a hacky but > expedient workaround. Here's one way to make otherwise equivalent > procedures distinguishable: > > Allocate a fresh tag using (list #f), and arrange for the procedure to > return that tag if it's called with a special input that's outside of > the normal domain. Note that for Scheme procedures, the "input" is in > general a list of arguments of arbitrary length. You could use > 'case-lambda', which creates procedures that evaluate different body > expressions depending on how many arguments are passed to it. Just add > a case for an arity that you will never use, which returns the unique > tag. > > In the example you gave, (let a () a) is equivalent to: > > ((letrec ((a (lambda () a))) > a)) > > The procedure returned by (let a () a) expects 0 arguments. It will > raise an error otherwise. We can repurpose the previously erroneous > arity-1 case to return the unique tag, as follows: > > (let ((unique-tag (list #f))) > ((letrec ((a (case-lambda > (() a) > ((x) unique-tag)))) > a))) > > Every time the above expression is evaluated, it will necessarily return > a unique procedure, which, if passed 0 arguments, behaves the same as > the procedure returned by (let a () a). > > Mark > Great, I've adapted your example to our code and it works nice. Thank you very much! -- Vladimir (λ)επτόν EDA — https://github.com/lepton-eda ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Self-evaluating function and closure 2019-06-17 8:48 ` Vladimir Zhbanov @ 2019-06-17 14:21 ` Mark H Weaver 0 siblings, 0 replies; 14+ messages in thread From: Mark H Weaver @ 2019-06-17 14:21 UTC (permalink / raw) To: guile-user Hi Vladimir, Vladimir Zhbanov <vzhbanov@gmail.com> writes: > Great, I've adapted your example to our code and it works > nice. Thank you very much! You're welcome, and I'm glad to hear it :) Best, Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2019-06-17 14:21 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2019-06-12 20:29 Self-evaluating function and closure Vladimir Zhbanov 2019-06-15 19:35 ` John Cowan 2019-06-16 0:39 ` Mark H Weaver 2019-06-16 0:36 ` Mark H Weaver 2019-06-16 9:02 ` Vladimir Zhbanov 2019-06-16 10:09 ` Mark H Weaver 2019-06-17 8:03 ` Vladimir Zhbanov 2019-06-16 9:47 ` Mark H Weaver 2019-06-16 10:21 ` Thomas Morley 2019-06-16 10:32 ` Mark H Weaver 2019-06-16 10:42 ` Thomas Morley 2019-06-16 11:29 ` Mark H Weaver 2019-06-17 8:48 ` Vladimir Zhbanov 2019-06-17 14:21 ` Mark H Weaver
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).