Discussion:
Aliasing an identifier
Marc Nieper-Wißkirchen
2018-11-15 16:55:15 UTC
Permalink
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=?'.

The following is one use case: I have written a macro `custom-quasiquote`,
which has a similar syntax and does similar things as `quasiquote' does.
Because I would like to use the special reader syntax for `quasiquote', I
would do:

(letrec-syntax ((quasiquote <custom-quasiquote-transformer>))
<body>)

In the <body>, whenever I write ``<template>', my
<custom-quasiquote-transformer> is being applied to <template>.

There may be parts in the <body> where I would like to use Scheme's
quasiquotation. One try is to surround these parts with:

(let-syntax ((quasiquote (identifier-syntax scheme-quasiquote)))
<expression>)

Here, `scheme-quasiquote' is bound to `quasiquote' as exported by
`(guile)'. However, this solution is not correct as the `quasiquote' local
to <expression> is not `free-identifier=?' to `scheme-quasiquote'. Thus
quasiquotations containing quasiquotes (that should become auxiliary
syntax) won't work right.

What I really need is to make the inner `quasiquote' a true alias of
`scheme-quasiquote' (or rather to restore the binding of `quasiquote' in
<expression> to what the binding outside the outer `letrec-syntax' was).

Chez Scheme has `alias' for this purpose:
https://cisco.github.io/ChezScheme/csug9.5/syntax.html#./syntax:h10.

What can I do in Scheme? If this is currently impossible, please consider
this post as a feature request for `alias' or a similar binding construct.
:-)

-- Marc
Marc Nieper-Wißkirchen
2018-11-17 15:17:06 UTC
Permalink
Am Do., 15. Nov. 2018 um 17:55 Uhr schrieb Marc Nieper-Wißkirchen <
Post by Marc Nieper-Wißkirchen
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=?'.
The following is one use case: I have written a macro `custom-quasiquote`,
which has a similar syntax and does similar things as `quasiquote' does.
Because I would like to use the special reader syntax for `quasiquote', I
(letrec-syntax ((quasiquote <custom-quasiquote-transformer>))
<body>)
In the <body>, whenever I write ``<template>', my
<custom-quasiquote-transformer> is being applied to <template>.
There may be parts in the <body> where I would like to use Scheme's
(let-syntax ((quasiquote (identifier-syntax scheme-quasiquote)))
<expression>)
Here, `scheme-quasiquote' is bound to `quasiquote' as exported by
`(guile)'. However, this solution is not correct as the `quasiquote' local
to <expression> is not `free-identifier=?' to `scheme-quasiquote'. Thus
quasiquotations containing quasiquotes (that should become auxiliary
syntax) won't work right.
What I really need is to make the inner `quasiquote' a true alias of
`scheme-quasiquote' (or rather to restore the binding of `quasiquote' in
<expression> to what the binding outside the outer `letrec-syntax' was).
https://cisco.github.io/ChezScheme/csug9.5/syntax.html#./syntax:h10.
What can I do in Scheme? If this is currently impossible, please consider
this post as a feature request for `alias' or a similar binding construct.
:-)
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.

-- Marc
Marc Nieper-Wißkirchen
2018-11-18 11:15:42 UTC
Permalink
Am Sa., 17. Nov. 2018 um 16:17 Uhr schrieb Marc Nieper-Wißkirchen <
Post by Marc Nieper-Wißkirchen
Am Do., 15. Nov. 2018 um 17:55 Uhr schrieb Marc Nieper-Wißkirchen <
Post by Marc Nieper-Wißkirchen
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
(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
(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

Loading...