[Top][All Lists]

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

Re: define-module, #:export and export

From: Jean Abou Samra
Subject: Re: define-module, #:export and export
Date: Thu, 5 Jan 2023 03:07:10 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.6.0

Le 04/01/2023 à 18:28, Maxime Devos a écrit :
On 04-01-2023 16:11, yarl baudig wrote:
Hello guile.

I don't know if that's a bug. Anyway, I am confused about this so I ask. I came across this problem playing with guix source code. I will share different "tests" each test is a directory with nothing but the files I share. each time the command to try the test (inside it's directory) is `guile --no-auto-compile -L . main.scm`
My (untested) hypothesis (*: things I'm unsure about)

If there is no #:export (...), then when
(define-operation (valid-path?)) is expanded, the identifier 'valid-path?' is free (*).

Hence, the following ...

>         (syntax-rules (name ...)
>           ((_ name) id) ...)))))

refers to the free identifier 'valid-path?'.

Conversely, if there is #:export, then the (syntax-rules (name ...) ...) looks for the bound identifier 'valid-path?' (*).

Important: bound identifiers != free-identifier, even if they have the same symbol!  For (simple-format #t "~S\n" (operation-id valid-path?)) to work, the 'valid-path?' of that expression must be bound if the syntax-rules looks for a bound identifier, and free if it looks for a free identifier.

That is also my understanding, confirmed by

$ cat lib.scm
(define-module (lib)
  #:export (test))

(define-syntax test
  (lambda (sintax)
    (syntax-case sintax ()
      ((test id)
       (datum->syntax sintax (free-identifier=? #'id #'thing))))))

$ cat test.scm
(define-module (main)
  #:use-module (lib)
  #:export (thing)

(display (test thing))

(define thing 5)

$ guile3.0 -L . test.scm

If you comment out #:export (thing), the result changes to #t.

To put it perhaps more simply, the use of #:export causes Guile to understand early that there will be a variable 'thing' in this module, and makes the identifier 'thing' refer to this variable that is not yet defined. However, hygiene implies that you want to be able to use keywords as if they were not keywords if they are rebound, e.g. the 'else' here doesn't cause the cond clause to be taken:

(let ((else #f)) (cond (#f 'bla) (else 'foo) (#t 'bar)))
$1 = bar

The way this is done is by comparing with the original identifier given to syntax-rules. Technically, they are compared with free-identifier=? . This means that a use of the identifier matches the keyword iff both are unbound, or both are bound to the same lexical binding. However, this isn't the case here, as the keyword in the macro was unbound, but at the point of use, it has been bound by #:export.

Honestly, I think it is better to choose a different way of writing these macros that avoids this confusing issue. Try defining the operations at the same time as the enum so that the macro giving an enum member refers to the bindings of the operators. If you give more context on what you're trying to do, we could help more.


Attachment: OpenPGP_signature
Description: OpenPGP digital signature

reply via email to

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