29
78
Fork
You've already forked r7rs
2

Self-quoting empty list #128

Closed
opened 2023年07月26日 11:00:50 +02:00 by mnw · 17 comments
Member
Copy link

As () is no valid expression, there is no well-founded reason why the empty list is not self-quoting like characters, numbers, strings, etc.

Every valid Scheme expression that is not a self-quoting literal is of the form <identifier> or (<keyword> . <datum>) or (<expression> . <datum>).

We can therefore make () a self-quoting literal, evaluating to the empty list. This increases the consistency of Scheme.

As `()` is no valid expression, there is no well-founded reason why the empty list is not self-quoting like characters, numbers, strings, etc. Every valid Scheme expression that is not a self-quoting literal is of the form `<identifier>` or `(<keyword> . <datum>)` or `(<expression> . <datum>)`. We can therefore make `()` a self-quoting literal, evaluating to the empty list. This increases the consistency of Scheme.

Empty lists were self-quoting at least up to R3RS; in R4RS they were explicitly made syntax errors. As usual, we don't know exactly why, but I am willing to believe that there was a good reason.

Empty lists were self-quoting at least up to R3RS; in R4RS they were explicitly made syntax errors. As usual, we don't know exactly why, but I am willing to believe that there was a good reason.
Author
Member
Copy link

Empty lists were self-quoting at least up to R3RS; in R4RS they were explicitly made syntax errors. As usual, we don't know exactly why, but I am willing to believe that there was a good reason.

Even if there was a good reason, it may very well be that there is no longer one, especially if we can't think of one.

> Empty lists were self-quoting at least up to R3RS; in R4RS they were explicitly made syntax errors. As usual, we don't know exactly why, but I am willing to believe that there was a good reason. Even if there *was* a good reason, it may very well be that there is no longer one, especially if we can't think of one.
Owner
Copy link

It would be worth asking people who are around at the time.

Personally I would be against restoring self-evaluating (), as requiring it to be quoted helps catch errors where something that was meant to be a procedure call was not filled in. This is a rare error when writing code by hand, but can help find faulty macro expansions: in Chibi I’ve occasionally had the ‘empty application’ error come out of a macro I was writing. If () were self-quoting, any problem with the macro would be somewhat more mysterious.

It would be worth asking people who are around at the time. Personally I would be against restoring self-evaluating `()`, as requiring it to be quoted helps catch errors where something that was meant to be a procedure call was not filled in. This is a rare error when writing code by hand, but can help find faulty macro expansions: in Chibi I’ve occasionally had the ‘empty application’ error come out of a macro I was writing. If `()` were self-quoting, any problem with the macro would be somewhat more mysterious.
Owner
Copy link

Aren't there mailing list archives we could check?

Aren't there mailing list archives we could check?
Author
Member
Copy link

Empty lists were self-quoting at least up to R3RS; in R4RS they were explicitly made syntax errors. As usual, we don't know exactly why, but I am willing to believe that there was a good reason.

On page 7, top of the second column of the R3RS it says: "Note: In many dialects of Lisp, the empty combination, (), is a legitimate expression. In Scheme, combinations must have at least one sub expression, so () is not a syntactically valid expression."

So it seems that the empty list were not self-quoting in R3RS and that no change was made from R3RS to R4RS.

At least in/since R6RS, an expression of the form (<keyword> . <datum>) can be a syntactically valid expression so that the old identification of "combination = list" no longer holds. The cutting line is between pair and non-pairs. (Note also that R3RS speaks of "sub expression"; this no longer makes sense at least since R5RS when macros became an official part of the language.)

> Empty lists were self-quoting at least up to R3RS; in R4RS they were explicitly made syntax errors. As usual, we don't know exactly why, but I am willing to believe that there was a good reason. On page 7, top of the second column of the R3RS it says: "Note: In many dialects of Lisp, the empty combination, (), is a legitimate expression. In Scheme, combinations must have at least one sub expression, so () is not a syntactically valid expression." So it seems that the empty list were not self-quoting in R3RS and that no change was made from R3RS to R4RS. At least in/since R6RS, an expression of the form `(<keyword> . <datum>)` can be a syntactically valid expression so that the old identification of "combination = list" no longer holds. The cutting line is between pair and non-pairs. (Note also that R3RS speaks of "sub expression"; this no longer makes sense at least since R5RS when macros became an official part of the language.)
Author
Member
Copy link

I found the following in R2RS (page 29):

The () notation must be quoted in programs, however, because otherwise it would be a procedure call without a [sic] expression in the procedure position.

This is, of course, nonsense as the grammar for Scheme given on pages 6/7 does not classify () as a procedure call at all.

I found the following in [R2RS](https://dspace.mit.edu/bitstream/handle/1721.1/5600/AIM-848.pdf) (page 29): > The () notation must be quoted in programs, however, because otherwise it would be a procedure call without a [sic] expression in the procedure position. This is, of course, nonsense as the grammar for Scheme given on pages 6/7 does not classify `()` as a procedure call at all.
Author
Member
Copy link

The only well-founded reason I see that speaks against making () a self-quoting constant is that should we ever implement Racket's #%app and #%datum in Scheme, the expander could treat () as (#%app) or as (#%datum ()). We would remove the first option (which in this form looks not so unattractive) in the future if we made () a self-quoting constant now (unless the then-standard #%app is defined so that (#%app) evaluates to ()).

The only well-founded reason I see that speaks against making `()` a self-quoting constant is that should we ever implement Racket's `#%app` and `#%datum` in Scheme, the expander could treat `()` as `(#%app)` or as `(#%datum ())`. We would remove the first option (which in this form looks not so unattractive) in the future if we made `()` a self-quoting constant now (unless the then-standard `#%app` is defined so that `(#%app)` evaluates to `()`).

I think that's precisely the point being made by the various standards: that a pair is always #%app, never #%datum, and there is no reason why (#%app) should return (), or anything else.

I think that's precisely the point being made by the various standards: that a pair is always `#%app`, never `#%datum`, and there is no reason why `(#%app)` should return `()`, or anything else.
Member
Copy link

Another thing to consider is that allowing for a self-quoting () would be rather weird when we consider this in context of other Lisps. Other Lisps allow for this, so allowing this would be Scheme being different for the sake of being different.

Another thing to consider is that allowing for a self-quoting `()` would be rather weird when we consider this in context of other Lisps. Other Lisps allow for this, so allowing this would be Scheme being different for the sake of being different.
Author
Member
Copy link

I think that's precisely the point being made by the various standards: that a pair is always #%app, never #%datum, and there is no reason why (#%app) should return (), or anything else.

I am not convinced. The idea of Racket's #%app is that every expression of the form (...) is a macro use. If the first entry is not a keyword, #%app is inserted so that it becomes a macro use. But there were no macros in early Schemes.

Anyway, I checked the Racket documentation and if I read it correctly, () expands into a use of #%datum, which supports the initial idea in that () could be safely evaluated as a constant literal.

Another thing to consider is that allowing for a self-quoting () would be rather weird when we consider this in context of other Lisps. Other Lisps allow for this, so allowing this would be Scheme being different for the sake of being different.

I am confused. Do other Lisps evaluate () as the empty list or not?

> I think that's precisely the point being made by the various standards: that a pair is always #%app, never #%datum, and there is no reason why (#%app) should return (), or anything else. I am not convinced. The idea of Racket's `#%app` is that every expression of the form `(...)` is a macro use. If the first entry is not a keyword, `#%app` is inserted so that it becomes a macro use. But there were no macros in early Schemes. Anyway, I checked the Racket documentation and if I read it correctly, `()` expands into a use of `#%datum`, which supports the initial idea in that `()` could be safely evaluated as a constant literal. > Another thing to consider is that allowing for a self-quoting () would be rather weird when we consider this in context of other Lisps. Other Lisps allow for this, so allowing this would be Scheme being different for the sake of being different. I am confused. Do other Lisps evaluate `()` as the empty list or not?

I am not convinced. The idea of Racket's #%app is that every expression of the form (...) is a macro use. If the first entry is not a keyword, #%app is inserted so that it becomes a macro use.

Right. But () is precisely not a macro use.

Anyway, I checked the Racket documentation and if I read it correctly, () expands into a use of #%datum, which supports the initial idea in that () could be safely evaluated as a constant literal.

Sure it could. It could also be evaluated as the constant literal 0, or "null", or anything else. It's arbitrary to treat () as a datum and all other cases of (...) as macro calls.

I am confused. Do other Lisps evaluate () as the empty list or not?

They do. But that is because () is an alternative lexical syntax for the variable nil, which is constantly bound to the empty list. By CL convention, nil is false, () is used for things like empty lambda-lists, and `'()' is an empty-list datum.

> I am not convinced. The idea of Racket's `#%app` is that every expression of the form `(...)` is a macro use. If the first entry is not a keyword, `#%app` is inserted so that it becomes a macro use. Right. But `()` is precisely *not* a macro use. > Anyway, I checked the Racket documentation and if I read it correctly, `()` expands into a use of `#%datum`, which supports the initial idea in that `()` could be safely evaluated as a constant literal. Sure it could. It could also be evaluated as the constant literal `0`, or `"null"`, or anything else. It's arbitrary to treat `()` as a datum and all other cases of `(...)` as macro calls. >> I am confused. Do other Lisps evaluate `()` as the empty list or not? They do. But that is because `()` is an alternative lexical syntax for the variable `nil`, which is constantly bound to the empty list. By CL convention, `nil` is false, `()` is used for things like empty lambda-lists, and `'()' is an empty-list datum.
Author
Member
Copy link

Anyway, I checked the Racket documentation and if I read it correctly, () expands into a use of #%datum, which supports the initial idea in that () could be safely evaluated as a constant literal.

Sure it could. It could also be evaluated as the constant literal 0, or "null", or anything else. It's arbitrary to treat () as a datum and all other cases of (...) as macro calls.

It is not (non-empty) lists that are macro calls (in which case I would agree with your argument), but pairs (and identifiers).

I am confused. Do other Lisps evaluate () as the empty list or not?

They do. But that is because () is an alternative lexical syntax for the variable nil, which is constantly bound to the empty list. By CL convention, nil is false, () is used for things like empty lambda-lists, and `'()' is an empty-list datum.

So rebinding nil and evaluating () yields the value to which nil is bound? Is rebinding nil even possible?

> > Anyway, I checked the Racket documentation and if I read it correctly, `()` expands into a use of `#%datum`, which supports the initial idea in that `()` could be safely evaluated as a constant literal. > > Sure it could. It could also be evaluated as the constant literal `0`, or `"null"`, or anything else. It's arbitrary to treat `()` as a datum and all other cases of `(...)` as macro calls. It is not (non-empty) *lists* that are macro calls (in which case I would agree with your argument), but *pairs* (and identifiers). > > >> I am confused. Do other Lisps evaluate `()` as the empty list or not? > > They do. But that is because `()` is an alternative lexical syntax for the variable `nil`, which is constantly bound to the empty list. By CL convention, `nil` is false, `()` is used for things like empty lambda-lists, and `'()' is an empty-list datum. So rebinding `nil` and evaluating `()` yields the value to which `nil` is bound? Is rebinding `nil` even possible?

So rebinding nil and evaluating () yields the value to which nil is bound? Is rebinding nil even possible?

CL has a defconstant declaration, which declares a variable and assigns it to a specified variable. Constants can neither be assigned nor rebound. CL behaves as if the following declarations were present:

(defconstant nil 'nil)
(defconstant t 't)
(defconstant internal-time-units-per-second ...)
(defconstant array-dimension-limit 7) ; or more
(defconstant lambda-list-keywords '(&allow-other-keys,
 &aux, &body, &environment, &key, &optional, &rest,
 &whole))
(defconstant array-total-size-limit 1024) ; usually much larger
...

So no.

> So rebinding `nil` and evaluating `()` yields the value to which `nil` is bound? Is rebinding `nil` even possible? CL has a `defconstant` declaration, which declares a variable and assigns it to a specified variable. Constants can neither be assigned nor rebound. CL behaves as if the following declarations were present: ``` (defconstant nil 'nil) (defconstant t 't) (defconstant internal-time-units-per-second ...) (defconstant array-dimension-limit 7) ; or more (defconstant lambda-list-keywords '(&allow-other-keys, &aux, &body, &environment, &key, &optional, &rest, &whole)) (defconstant array-total-size-limit 1024) ; usually much larger ... ``` So no.
Author
Member
Copy link

So rebinding nil and evaluating () yields the value to which nil is bound? Is rebinding nil even possible?

CL has a defconstant declaration, which declares a variable and assigns it to a specified variable. Constants can neither be assigned nor rebound. CL behaves as if the following declarations were present:

(defconstant nil 'nil)
(defconstant t 't)
(defconstant internal-time-units-per-second ...)
(defconstant array-dimension-limit 7) ; or more
(defconstant lambda-list-keywords '(&allow-other-keys,
 &aux, &body, &environment, &key, &optional, &rest,
 &whole))
(defconstant array-total-size-limit 1024) ; usually much larger
...

So no.

Thanks for explaining this to me!

So Scheme would effectively just do what CL does if () were evaluated to the empty list. (That does not rule out a library exporting nil bound to syntax that evaluates to () as well.)

> > So rebinding `nil` and evaluating `()` yields the value to which `nil` is bound? Is rebinding `nil` even possible? > > CL has a `defconstant` declaration, which declares a variable and assigns it to a specified variable. Constants can neither be assigned nor rebound. CL behaves as if the following declarations were present: > > ``` > (defconstant nil 'nil) > (defconstant t 't) > (defconstant internal-time-units-per-second ...) > (defconstant array-dimension-limit 7) ; or more > (defconstant lambda-list-keywords '(&allow-other-keys, > &aux, &body, &environment, &key, &optional, &rest, > &whole)) > (defconstant array-total-size-limit 1024) ; usually much larger > ... > ``` > > So no. Thanks for explaining this to me! So Scheme would effectively just do what CL does if `()` were evaluated to the empty list. (That does not rule out a library exporting `nil` bound to syntax that evaluates to `()` as well.)

So Scheme would effectively just do what CL does if () were evaluated to the empty list.

Yes. But the decision to make () a syntax error is very old in Scheme, and the designers were well aware that in CL, Maclisp, Interlisp, Zetalisp, etc. etc. () was self-evaluating. I have not been able to find an explanation of why they did it, but R2RS says this:

'() nd #!null are notations for the empty list. The #!null notation does not have to be quoted in programs. The () notation must be quoted in programs, however, because otherwise it would be a procedure call without a expression in the procedure position.

Rationale: Because many current Scheme interpreters deal with expressions as list structures rather than as character strings, they will treat an unquoted () as though it were quoted. It is entirely possible, however, that some implementations of Scheme will be able to detect an unquoted () as an error.

I would think more than twice before reversing this decision after almost fifty years.

> So Scheme would effectively just do what CL does if `()` were evaluated to the empty list. Yes. But the decision to make `()` a syntax error is very old in Scheme, and the designers were well aware that in CL, Maclisp, Interlisp, Zetalisp, etc. etc. `()` was self-evaluating. I have not been able to find an explanation of why they did it, but R2RS says this: > `'() ` nd `#!null` are notations for the empty list. The `#!null` notation does not have to be quoted in programs. The `()` notation must be quoted in programs, however, because otherwise it would be a procedure call without a expression in the procedure position. > > Rationale: Because many current Scheme interpreters deal with expressions as list structures rather than as character strings, they will treat an unquoted `()` as though it were quoted. It is entirely possible, however, that some implementations of Scheme will be able to detect an unquoted `()` as an error. I would think more than twice before reversing this decision after almost fifty years.
Member
Copy link

WG2 voted unanimously against self-quoting empty lists in the 2025年11月21日 meeting.

WG2 voted unanimously against self-quoting empty lists in the 2025年11月21日 meeting.

Empty list should not be quoted in definition of syntax. It creates a kind of schizophreny. IMO it should be explain somewhere that empty list must not be quoted in syntax pattern because there () is not '()

Empty list should not be quoted in definition of syntax. It creates a kind of schizophreny. IMO it should be explain somewhere that empty list must not be quoted in syntax pattern because there () is not '()
Sign in to join this conversation.
No Branch/Tag specified
main
pages
proc-draft
c-trichotomy
global-id-prop-ref
allow-van-tonder-like-expanders
No results found.
Labels
Clear labels
Batteries
Issues for the Batteries volume of the report, providing a standard library
Cleanup
Issues where inconsistencies or other infelicities have arisen
Consent Docket
Items which will be automatically be given a certain resolution if no objection is made by a certain date
Environments
Issues for the Environments volume of the report, providing OS interfaces and similar
Foundations
Issues for the Foundations volume of the report, related to the core language semantics
Macrological Fascicle
Meta
Procedural and organisational issues
Multiple proposals exist
Multiple competing, concrete proposals to resolve this issue exist (whether they are SRFI or still only pre-SRFI)
Pie-in-the-sky ideas
Ideas currently lacking any form of worked-out proposal
Problems without solutions
Problems we *must* solve but haven’t worked out any solution for yet
Procedural Fascicle
Public Comment
Publications
Issues related to the practicalities of making the report available
Question
For information and clarification
R6RS Compatibility
Issue touches on questions of R6RS compatibility
R7RS-small
Issues with the Small language Report
Resolution in draft
A resolution (not necessarily final) is specified in the current draft of the relevant fascicle
Specification exists
Proposals for which a viable, but incomplete specification text exists
SRFI/RnRS spec exists
A SRFI for this proposal exists, or a solution from another RnRS
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
7 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
scheme/r7rs#128
Reference in a new issue
scheme/r7rs
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?