212: Aliases

by Marc Nieper-Wißkirchen

Status

This SRFI is currently in final status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-212@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

Abstract

This SRFI introduces alias definitions, a syntactic extension. An alias definition transfers the binding of one identifier to another, effectively aliasing the identifier.

Rationale

In standard Scheme, an identifier can either name a type of syntax or a location where a value can be stored. In the former case, the identifier is said to be bound to a transformer for that syntax, and in the latter case, the identifier is said to be bound to the location.

The (lexical) binding of an identifier is an expand-time property of the identifier, in contrast to the (dynamic) value stored in a location, which is a run-time property of the location.

New bindings are established through variable or syntax definitions. For example, a variable definition

    (define variable expression)

creates a new location, binds the identifier variable to it and stores the value of expression in the location.

In particular,

    (define variable1 variable2)

creates a new location for variable1 and copies the value of variable2 into it. Subsequently modifying the value of variable2 won't modify the value of variable1.

In contrast, the alias definition introduced in this SRFI transfers an existing binding to an identifier and doesn't create a new one. For example,

    (alias variable1 variable2)

doesn't create a new location for variable1, but binds variable1 to the location of variable2. In particular, subsequently modifying the value of variable2 will modify the value of variable1 accordingly.

Examples

Aliases allow one to locally alias identifiers under short names without changing the expanded version of the program:

    (define *important-global-variable* '())
    (define (setup!)
      (alias ls *important-global-variable*)
      (set! ls (cons 1 ls))
      (set! ls (cons 2 ls)))
    (setup!)
    *important-global-variable*                ⟹ (2 1)

Aliasing an unbound identifier is an error.

    (alias x y)   ⟹ error

Rebinding the aliased identifier doesn't change the binding of the aliasing identifier:

    (let ((y +))
      (alias x y)
      (let ((y *))
        (free-identifier=? #'x #'y)))   ⟹ #f

In particular, aliasing doesn't make the aliasing identifier equivalent to the aliased one in the sense that binding one would bind the other:

    (let ((y +))
      (alias x y)
      (bound-identifier=? #'x #'y))   ⟹ #f

The following expression, however, is invalid as it is an error for a definition to define an identifier whose binding has to be known in order to determine the meaning of any preceding definition that belongs to the same group of (internal) definitions:

    (let ((y +))
      (alias x y)
      (define y *)
      (free-identifier=? #'x #'y))   ⟹ error

Aliases can also be used to alias auxiliary syntax:

    (alias inject unquote)
    `(list (inject (+ 1 2)) 4)   ⟹ (list 3 4)

In macros, they can be used to inject bindings from the macro environment into the use environment:

    (let ((y 1))
      (let-syntax ((inject-y
                    (syntax-rules ()
                      ((inject-y x) (alias x y)))))
        (let ((y 2))
          (inject-y x)
          (set! x (* 3 x)))
        y))                                          ⟹ 3

Finally, the use of aliases is not limited to variables and keywords, but can also be used with other bindings like pattern variables:

    (syntax-case #'pear ()
      (pvar
       (let* ()
         (alias fruit pvar)
         (syntax->datum #'(a fruit)))))   ⟹ (a pear)

Specification

Syntax

The alias definition is used to transfer the binding of one identifier to another. Like other definitions, it can appear either at the outermost level or in a body where other definitions can appear.

(alias identifier1 identifier2)

The binding of identifier2 is transferred to identifier1. This means that if identifier2 is bound to the location of a variable, the location of a pattern variable, a type of syntax, a type of syntax parameter, or any other quantity, identifier1 will be bound to the same quantity. If identifier2 is unbound, it is an error.

Implementation

A portable Scheme implementation is not possible. An implementation of SRFI 212 is available in Unsyntax.

Chez Scheme implements alias in its (chezscheme) library.

The Kawa Scheme language also contains an implementation of alias, albeit under the name define-alias.

Test suite for an implementation.

Acknowledgements

The author got the idea from reading the manual of Chez Scheme by R. Kent Dybvig.

© 2020 Marc Nieper-Wißkirchen.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: Arthur A. Gleckler