guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Self-evaluating function and closure


From: Mark H Weaver
Subject: Re: Self-evaluating function and closure
Date: Sun, 16 Jun 2019 06:09:02 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2 (gnu/linux)

Hi Vladimir,

Vladimir Zhbanov <address@hidden> writes:

> On Sat, Jun 15, 2019 at 08:36:34PM -0400, Mark H Weaver wrote:
>> Vladimir Zhbanov <address@hidden> 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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]