unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* 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-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-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-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-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: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  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 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-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).