guile-devel
[Top][All Lists]
Advanced

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

Re: Aliasing an identifier


From: Marc Nieper-Wißkirchen
Subject: Re: Aliasing an identifier
Date: Sun, 18 Nov 2018 12:15:42 +0100

Am Sa., 17. Nov. 2018 um 16:17 Uhr schrieb Marc Nieper-Wißkirchen <address@hidden>:
Am Do., 15. Nov. 2018 um 17:55 Uhr schrieb Marc Nieper-Wißkirchen <address@hidden>:
I would like to alias an identifier in Guile. By this, I mean the following: Given a bound identifier `x', I want to lexically introduce another identifier `y' with the same binding as `x' so that `x' and `y' become `free-identifier=?'.

Typo. "What can I in GUILE?" was the question I intended to ask. :-)
 
I gave it a try to implement `define-alias' in Guile. My first approximation to a solution was to add a clause like

((define-alias-form)
  (let ((id (wrap value w mod))
        (label (id-var-name e w mod)))
    (extend-ribcage! ribcage id label)
    (parse (cdr body) (cons id ids) labels var-ids vars vals bindings)))

to `expand-body' in `psyntax.scm'. (A similar clause is added to `expand-top-sequence' and `syntax-type' is extended to recognize `define-alias' and return `define-alias-form' for the type.)

With identifiers lexically bound in the same module, my change does what it is supposed to do. The following test passes:

(pass-if "alias is free-identifier=?"
  (let ((x #t))
    (define-syntax foo
      (syntax-rules (x)
        ((foo x) #t)
        ((foo _) #f)))
    (let ()
      (define-alias y x)
      (foo y))))
  
My code, however, doesn't work when the aliased identifier is global and/or comes from another module: The following test fails:

(pass-if-equal "alias is free-identifier=? with globals"
    '(1 5)
  (let ()
    (define-alias comma unquote)
    `(1 (comma (+ 2 3)))))

I see that my attempt to bind the identifier `y' to `x's label (if given `(define-alias y x)') is too naive. I have taken a look at `free-id=?' and `resolve-identifier' in `psyntax.scm', but the way modules are handled in `psyntax.scm' is still new to me. Thus I'm glad about any hints.

The problem seems to lie in the code of `free-id=?' in `psyntax.scm'. `free-id=?' contains the following clause:

((symbol? ni)
  ;; `i' is not lexically bound.  Assert that `j' is free,
  ;; and if so, compare their bindings, that they are either
  ;; bound to the same variable, or both unbound and have
  ;; the same name.
  (and (eq? nj (id-sym-name j))
       (let ((bi (id-module-binding i mi)))
         (if bi
             (eq? bi (id-module-binding j mj))
             (and (not (id-module-binding j mj))
                  (eq? ni nj))))
       (eq? (id-module-binding i mi) (id-module-binding j mj))))

`free-id=?' is called by the transformer of `quasiquote' to check whether `comma' and `unquote' are `free-identifier=?'. If I understand the code of `free-id=?' correctly, `ni' and `nj' become the labels of `comma' and `unquote', respectively, which are both the same due to how I have tried to implement `define-alias'. These labels are symbols because they are not lexically bound but (imported) globals (from the module `(guile)').

The code from `free-id=?' cited above looks up the module bindings of `comma' and `unquote' but not by their (symbol) labels, but by their identifier names. This fails because there is no global identifier bound with the name `comma'. (Independently, too things about the code look strange to me. Firstly, the code seems to make the relation defined by `free-id=?' non-symmetric: For the second identifier `j', it is checked whether its symbol name is the same as its label. This does not happen for the identifier `i'. Secondly, the last `eq?' comparison seems to be superfluous.)

I have rewritten the above clause as follows:

((symbol? ni)
  ;; `i' is not lexically bound.  Assert that `j' is free,
  ;; and if so, compare their bindings, that they are either
  ;; bound to the same variable, or both unbound and have
  ;; the same name.
  (and (symbol? nj)
       (let ((bi (id-module-binding ni mi)))
         (if bi
             (eq? bi (id-module-binding nj mj))
             (and (not (id-module-binding nj mj))
               (eq? ni nj)))))))

The main difference is that I use `ni' and `nj' (the symbol labels) to look up the binding (I also have to check explicitly that the label of `j' also makes `j' as not lexically bound.)

I think this implements the correct semantics of free-id=?. And, indeed, the following test now passes:

(pass-if-equal "alias is free-identifier=? with globals"
    '(1 5)
  (let ()
    (define-alias comma unquote)
    `(1 (comma (+ 2 3)))))

Also this test now works correctly:

(pass-if "alias is free-identifier=? with unbound"
  (let ()
    (define-syntax foo
      (syntax-rules (x z)
        ((foo z) #f)
        ((foo x) #t)
        ((foo _) #f)))
    (let ()
      (define-alias y x)
      (foo y))))

The new semantics `free-id=?' are compatible with all existing tests in the Guile test suite. If you want to experiment with my ideas, I have attached a patch file.
 
-- Marc

Attachment: define-alias.patch
Description: Text Data


reply via email to

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